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

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,
                ),
              ),
            );
          }),
        ),
      ),
    );
  }
}

关键修正点

  1. 动画阶段拆分:用TweenSequence明确划分进入、停留、离开三个阶段,避免手动控制控制器启停的不可靠性
  2. 时间线精准控制:通过计算各阶段在总时长中的比例,确保每个容器依次进入,全部到位后统一停留再离开
  3. 优化AnimatedBuilder:将静态的Container作为child传入AnimatedBuilder,减少不必要的widget重建
  4. 循环逻辑简化:仅通过状态监听实现动画完成后的自动循环,无需复杂的listener判断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:40:39