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

Flutter中如何实现倒计时期间的旋转圆形动画?

实现与倒计时同步的圆形旋转动画

要实现30秒倒计时期间圆形持续旋转、结束后停止,点击重启同步恢复的效果,你可以通过AnimationController结合Bloc状态监听来实现,以下是具体修改方案:

核心思路

  1. 将原ResendLoader改为StatefulWidget,用于持有动画控制器(Stateless无法维护动画状态)。
  2. 初始化与倒计时时长一致的AnimationController,通过RotationTransition实现圆形旋转。
  3. 监听TimerBloc的状态变化,在倒计时启动时重置并播放动画,倒计时结束时停止动画。

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
// 导入你的TimerBloc和constants文件

class ResendLoader extends StatefulWidget {
  const ResendLoader({Key? key}) : super(key: key);

  @override
  State<ResendLoader> createState() => _ResendLoaderState();
}

class _ResendLoaderState extends State<ResendLoader> with SingleTickerProviderStateMixin {
  late AnimationController _rotationController;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,时长与倒计时同步为30秒
    _rotationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 30),
    )..addStatusListener((status) {
        // 动画自然结束时自动停止(确保倒计时结束后动画不再运行)
        if (status == AnimationStatus.completed) {
          _rotationController.stop();
        }
      });
  }

  @override
  void dispose() {
    // 释放动画控制器,避免内存泄漏
    _rotationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 使用BlocListener监听状态变化,控制动画启停
    return BlocListener<TimerBloc, TimerState>(
      listener: (context, state) {
        if (state is TimerStarted) {
          // 倒计时启动时,重置动画并开始循环旋转
          _rotationController.reset();
          _rotationController.repeat();
        } else if (state is TimerCompleted) {
          // 倒计时结束时,停止旋转动画
          _rotationController.stop();
        }
      },
      child: BlocBuilder<TimerBloc, TimerState>(
        buildWhen: (prev, state) => prev.runtimeType != state.runtimeType,
        builder: (context, state) {
          return Center(
            child: Stack(
              alignment: AlignmentDirectional.center,
              children: [
                SizedBox(
                  width: 70,
                  height: 70,
                  // 用RotationTransition包裹CustomPaint,实现旋转效果
                  child: RotationTransition(
                    turns: _rotationController,
                    child: CustomPaint(
                      // 如果你需要用CirclePainter,替换这里的RingPainter即可
                      painter: RingPainter(
                        // 可选:根据剩余时长计算环形进度,让进度与倒计时同步
                        progress: state is TimerRunInProgress ? (state.duration / 30) : 0,
                        taskCompletedColor: Colors.white,
                        taskNotCompletedColor: constants.Colors.greyDark.withOpacity(0.5),
                      ),
                    ),
                  ),
                ),
                _timerText(context),
              ],
            ),
          );
        },
      ),
    );
  }

  Widget _timerText(BuildContext context) {
    final currentState = context.watch<TimerBloc>().state;
    if (currentState is TimerRunInProgress) {
      return Text(
        '${currentState.duration.toString().padLeft(2, '0')}:00',
        style: const TextStyle(color: Colors.white),
      );
    } else if (currentState is TimerCompleted) {
      return const Text('00:00', style: TextStyle(color: Colors.white));
    }
    return const Text('30:00', style: TextStyle(color: Colors.white));
  }
}

// 你的CirclePainter可以直接使用,放到RotationTransition里即可
class CirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final strokeWidth = size.width / 15.0;
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;
    var paint1 = Paint()
      ..color = const Color(0xff63aa65)
      ..style = PaintingStyle.fill;
    canvas.drawCircle(center, radius, paint1);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

关键说明

  • 动画控制器:AnimationController的时长设为30秒,与倒计时完全同步,repeat()方法让圆形持续旋转。
  • 状态监听:通过BlocListener监听TimerBloc的状态,在TimerStarted时重置并启动动画,TimerCompleted时停止动画。
  • 旋转实现:使用Flutter内置的RotationTransition组件,无需手动计算旋转角度,简化动画实现。
  • 内存管理:在dispose方法中释放AnimationController,避免内存泄漏。

重启逻辑

当点击重启按钮时,只需向TimerBloc发送TimerStarted(duration: 30)事件,BlocListener会自动监听到状态变化,重置并重新启动旋转动画,与倒计时同步恢复。

内容的提问来源于stack exchange,提问作者Max

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 20:09:21