import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:remixicon/remixicon.dart'; import '../viewmodel/plan_detail_store.dart'; class AvatarCard extends StatefulWidget { const AvatarCard({super.key}); @override State createState() => _AvatarCardState(); } class _AvatarCardState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; ///对话框值 String _dialog = ""; @override void initState() { super.initState(); //初始化动画 _controller = AnimationController( vsync: this, duration: Duration(milliseconds: 300), ); _animation = Tween(begin: 0, end: 1).animate( CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ), ); _initDialog(); } @override void dispose() { super.dispose(); _controller.dispose(); } ///初始化是否显示对话 void _initDialog() { WidgetsBinding.instance.addPostFrameCallback((_) { final store = context.read(); void listener() { if (store.planDetail.dialog != null && !store.showRoleTalk) { setState(() { _dialog = store.planDetail.dialog!; }); _toggleShow(); store.removeListener(listener); } } store.addListener(listener); }); } ///切换显示show void _toggleShow() { var store = context.read(); if (store.planDetail.dialog == null) { return; } setState(() { store.showRoleTalk = !store.showRoleTalk; if (store.showRoleTalk) { _controller.forward(); } else { _controller.reverse(); } }); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 30), child: Transform.translate( offset: Offset(0, 50), child: Column( children: [ BothSizeTransition( animation: _animation, offset: Offset(50, 50), child: Container( padding: EdgeInsets.only(bottom: 10), child: InkWell( onTap: _toggleShow, child: Stack( children: [ Container( width: double.infinity, padding: EdgeInsets.all(4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), border: Border.all(color: Colors.black, width: 1), ), child: Text( _dialog, style: TextStyle(fontSize: 12), textAlign: TextAlign.center, ), ), Positioned( bottom: 0, left: 0, right: 0, child: CustomPaint( painter: BubblePainter(), ), ), ], ), ), ), ), SizedBox( width: double.infinity, child: Stack( alignment: Alignment.bottomCenter, children: [ Image.asset("assets/image/kbn.png", height: 100), Positioned( top: 20, child: Transform.translate( offset: Offset(40, -10), child: GestureDetector( onTap: _toggleShow, child: BothSizeTransition( animation: ReverseAnimation(_animation), offset: Offset(-50, -50), child: Icon(RemixIcons.message_2_line), ), ), ), ), ], ), ), ], ), ), ); } } ///聊天气泡三角 class BubblePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { var bottomWidth = 10; //点坐标 var start = Offset((size.width - bottomWidth) / 2, 0); //底线 final bottomLinePaint = Paint() ..color = Colors.white ..strokeWidth = 2 ..style = PaintingStyle.stroke; final bottomLinePath = Path() ..moveTo(start.dx, 0) ..lineTo(start.dx + bottomWidth, 0); canvas.drawPath(bottomLinePath, bottomLinePaint); //边线 final sideLinePaint = Paint() ..color = Colors.black ..strokeWidth = 1 ..style = bottomLinePaint.style; final sideLinePath = Path() ..moveTo(start.dx, 0) ..lineTo(start.dx + bottomWidth / 2, 5) ..lineTo(start.dx + bottomWidth, 0); canvas.drawPath(sideLinePath, sideLinePaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } } ///缩放动画 class BothSizeTransition extends StatelessWidget { final Animation animation; final Widget child; final Offset offset; const BothSizeTransition({ super.key, required this.animation, this.offset = Offset.zero, required this.child, }); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, builder: (_, child) { return Align( alignment: Alignment(0.3, 1), child: Opacity( opacity: animation.value, child: Transform( alignment: Alignment(0, 1), transform: Matrix4.identity() ..translate( offset.dx * (1 - animation.value), offset.dy * (1 - animation.value), ) ..scale(animation.value, 1.0), child: SizeTransition( sizeFactor: animation, axis: Axis.vertical, child: child, ), ), ), ); }, child: child, ); } }