求助:如何在Flutter中实现直播PK对战动态分数条及图标动画
直播PK对战

我希望在Flutter中实现如图所示的分数条,使其能随分数增减动态变化,并为图标添加动画效果。若有了解相关实现方案的开发者,恳请提供帮助。
实现方案
1. 动态分数条核心实现
用AnimatedContainer配合Stack布局,通过计算分数占比动态调整两侧进度条宽度,自带平滑过渡动画:
class PkScoreBar extends StatefulWidget { final int leftScore; final int rightScore; const PkScoreBar({super.key, required this.leftScore, required this.rightScore}); @override State<PkScoreBar> createState() => _PkScoreBarState(); } class _PkScoreBarState extends State<PkScoreBar> { // 计算两侧分数占比 double get leftRatio => widget.leftScore / (widget.leftScore + widget.rightScore); double get rightRatio => widget.rightScore / (widget.leftScore + widget.rightScore); @override Widget build(BuildContext context) { final barWidth = MediaQuery.of(context).size.width; return Container( height: 24, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.grey[200], ), child: Stack( children: [ // 左侧进度条 AnimatedContainer( duration: const Duration(milliseconds: 500), width: barWidth * leftRatio, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.redAccent, ), ), // 右侧进度条 AnimatedContainer( duration: const Duration(milliseconds: 500), margin: EdgeInsets.only(left: barWidth * leftRatio), width: barWidth * rightRatio, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.blueAccent, ), ), // 中间动态图标 Positioned( left: barWidth * leftRatio - 16, top: -8, child: const AnimatedPkIcon(), ), ], ), ); } }
2. 图标动画实现
通过AnimationController和AnimatedBuilder实现缩放弹跳动画,分数变化时自动触发循环动画:
class AnimatedPkIcon extends StatefulWidget { const AnimatedPkIcon({super.key}); @override State<AnimatedPkIcon> createState() => _AnimatedPkIconState(); } class _AnimatedPkIconState extends State<AnimatedPkIcon> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _scaleAnim; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, )..repeat(reverse: true); // 循环往复动画 _scaleAnim = Tween<double>(begin: 1.0, end: 1.2).animate( CurvedAnimation(parent: _controller, curve: Curves.bounceInOut), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _scaleAnim, builder: (ctx, child) { return Transform.scale( scale: _scaleAnim.value, child: Container( width: 32, height: 32, decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 4)], ), child: const Icon(Icons.emoji_events, color: Colors.amber, size: 20), ), ); }, ); } }
3. 页面调用示例
在业务页面中传入分数,更新分数时调用setState即可触发动画:
class PkLivePage extends StatefulWidget { const PkLivePage({super.key}); @override State<PkLivePage> createState() => _PkLivePageState(); } class _PkLivePageState extends State<PkLivePage> { int leftScore = 60; int rightScore = 40; // 模拟分数更新 void addScore() { setState(() { leftScore += Random().nextInt(15); rightScore += Random().nextInt(15); }); } @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 60), child: Column( children: [ PkScoreBar(leftScore: leftScore, rightScore: rightScore), const SizedBox(height: 20), Text('主播A: $leftScore 分 | 主播B: $rightScore 分', style: const TextStyle(fontSize: 16)), const SizedBox(height: 30), ElevatedButton(onPressed: addScore, child: const Text('模拟加分')), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者qudrat ullah
相关产品推荐
相关产品推荐

