Flutter中如何实现倒计时期间的旋转圆形动画?
实现与倒计时同步的圆形旋转动画
要实现30秒倒计时期间圆形持续旋转、结束后停止,点击重启同步恢复的效果,你可以通过AnimationController结合Bloc状态监听来实现,以下是具体修改方案:
核心思路
- 将原
ResendLoader改为StatefulWidget,用于持有动画控制器(Stateless无法维护动画状态)。 - 初始化与倒计时时长一致的
AnimationController,通过RotationTransition实现圆形旋转。 - 监听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
相关产品推荐
相关产品推荐

