You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

直播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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 13:22:06