Flutter动画需求:暂停后续播,容器停中间再移至正偏移
Flutter 容器序列动画实现方案
你需要的是容器从左侧依次进入中间,等待全部到位后停留一段时间,再集体移到右侧的循环动画。原代码的问题在于手动控制动画控制器启停的逻辑错误,且动画时间线设计不符合需求,以下是修正后的实现:
核心思路
把整个动画分为三个阶段:
- 进入阶段:5个容器依次从左侧(负偏移)移动到中间位置,每个容器的进入动画错开启动
- 停留阶段:所有容器到达中间后,保持静止一段时间
- 离开阶段:所有容器一起从中间移动到右侧(正偏移)
- 动画完成后自动循环
修正后的代码
class _LoadingAnimationState extends State<LoadingAnimation> with TickerProviderStateMixin { late AnimationController _controller; late List<Animation<Offset>> _sequenceAnimations; // 定义各阶段时长 final Duration _enterDuration = const Duration(seconds: 2); final Duration _waitDuration = const Duration(seconds: 2); final Duration _exitDuration = const Duration(seconds: 2); @override void initState() { super.initState(); // 总动画时长 = 进入+停留+离开 final totalDuration = _enterDuration + _waitDuration + _exitDuration; _controller = AnimationController(vsync: this, duration: totalDuration); _sequenceAnimations = List.generate(5, (index) { // 每个容器的进入动画时长:进入阶段总时长 / 容器数量 final singleEnterDuration = _enterDuration.inMilliseconds / 5; // 计算各阶段在总时长中的比例 final enterStart = (index * singleEnterDuration) / totalDuration.inMilliseconds; final enterEnd = ((index + 1) * singleEnterDuration) / totalDuration.inMilliseconds; final waitEnd = (_enterDuration + _waitDuration).inMilliseconds / totalDuration.inMilliseconds; final exitEnd = 1.0; return TweenSequence<Offset>([ // 1. 进入阶段:从左侧到中间 TweenSequenceItem( tween: Tween(begin: const Offset(-300, 0), end: const Offset(0, 0)), weight: (enterEnd - enterStart) * 100, ), // 2. 停留阶段:保持在中间位置 TweenSequenceItem( tween: Tween(begin: const Offset(0, 0), end: const Offset(0, 0)), weight: (waitEnd - enterEnd) * 100, ), // 3. 离开阶段:从中间到右侧 TweenSequenceItem( tween: Tween(begin: const Offset(0, 0), end: const Offset(300, 0)), weight: (exitEnd - waitEnd) * 100, ), ]).animate(CurvedAnimation( parent: _controller, curve: Interval(0, 1, curve: Curves.easeInOut), )); }); // 动画完成后重置并循环 _controller.addStatusListener((status) { if (status == AnimationStatus.completed) { _controller.reset(); _controller.forward(); } }); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(5, (index) { return AnimatedBuilder( animation: _controller, builder: (context, child) { return Transform.translate( offset: _sequenceAnimations[index].value, child: child, ); }, child: Container( margin: const EdgeInsets.all(5), width: 10, height: 10, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.blue, ), ), ); }), ), ), ); } }
关键修正点
- 动画阶段拆分:用
TweenSequence明确划分进入、停留、离开三个阶段,避免手动控制控制器启停的不可靠性 - 时间线精准控制:通过计算各阶段在总时长中的比例,确保每个容器依次进入,全部到位后统一停留再离开
- 优化AnimatedBuilder:将静态的Container作为child传入
AnimatedBuilder,减少不必要的widget重建 - 循环逻辑简化:仅通过状态监听实现动画完成后的自动循环,无需复杂的listener判断
内容的提问来源于stack exchange,提问作者Jeevan Shrestha
相关产品推荐
相关产品推荐

