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

如何在Flutter中实现多组件带延迟的滑入滑出序列动画?

问题根因

你遇到的动画顺序颠倒问题,是flutter_sequence_animation包的默认值逻辑导致的:当某个动画tag的时间线存在空白段(即还未到该tag首个动画的起始时间时),包不会自动使用你定义的首个animatable的begin值,而是直接返回该tag最后一段动画的end值,这就导致最后定义的test元素在动画刚启动时就已经处于最终位置,看起来像是最先展示的。

解决方案

给每个动画tag都补一段从0到该tag首个动画起始时间的固定值补间,填补时间线的空白段,保证动画未启动时元素保持在初始的屏幕外位置。

修改后的完整代码如下:

import 'package:flutter/material.dart';
import 'package:flutter_sequence_animation/flutter_sequence_animation.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

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

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  late AnimationController controller;
  late SequenceAnimation sequenceAnimation;
  void initAnimation() {
    double screenSize = 500; // MediaQuery.of(context).size.width;

    controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 5500));
    sequenceAnimation = SequenceAnimationBuilder()
        // 填补source标签的初始空白段,保持初始值-100
        .addAnimatable(
          animatable: Tween<double>(begin: -100, end: -100),
          tag: 'source',
          curve: Curves.linear,
          from: Duration.zero,
          to: Duration.zero,
        )
        .addAnimatable(
          animatable: Tween<double>(begin: -100, end: 200),
          tag: 'source',
          curve: Curves.easeInBack,
          from: Duration(milliseconds: 0),
          to: Duration(milliseconds: 1000),
        )
        .addAnimatable(
          animatable: Tween<double>(begin: 200, end: screenSize),
          tag: 'source',
          curve: Curves.easeInBack,
          from: Duration(milliseconds: 1250),
          to: Duration(milliseconds: 2250),
        )
        // 填补plane标签的初始空白段,保持初始值-100
        .addAnimatable(
          animatable: Tween<double>(begin: -100, end: -100),
          tag: 'plane',
          curve: Curves.linear,
          from: Duration.zero,
          to: Duration(milliseconds: 2250),
        )
        .addAnimatable(
          animatable: Tween<double>(begin: -100, end: screenSize),
          from: Duration(milliseconds: 2250),
          to: Duration(milliseconds: 2500),
          curve: Curves.easeInBack,
          tag: 'plane',
        )
        // 填补test标签的初始空白段,保持初始值-200
        .addAnimatable(
          animatable: Tween<double>(begin: -200, end: -200),
          tag: 'test',
          curve: Curves.linear,
          from: Duration.zero,
          to: Duration(milliseconds: 3500),
        )
        .addAnimatable(
          animatable: Tween<double>(begin: -200, end: 200),
          tag: 'test',
          curve: Curves.easeInBack,
          from: Duration(milliseconds: 3500),
          to: Duration(milliseconds: 4500), 
        )
        .addAnimatableAfterLastOneWithTag(
          animatable: Tween<double>(begin: 200, end: screenSize),
          tag: 'test',
          curve: Curves.easeInBack,
          duration: Duration(milliseconds: 1000),
          lastTag: 'test', 
        )
        .animate(controller);

    controller.repeat();
  }

  @override
  void initState() {
    super.initState();
    initAnimation();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          AnimatedBuilder(
            animation: controller,
            builder: (context, child) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  height: 100,
                  width: 100,
                  transform: Matrix4.translationValues(
                    sequenceAnimation["source"].value,
                    0.0,
                    0.0,
                  ),
                ),
              ],
            ),
          ),
          AnimatedBuilder(
            animation: controller,
            builder: (context, child) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.red,
                  height: 100,
                  width: 100,
                  transform: Matrix4.translationValues(
                    sequenceAnimation["test"].value,
                    0.0,
                    0.0,
                  ),
                  child: Text('Test'),
                ),
              ],
            ),
          ),
          AnimatedBuilder(
            animation: controller,
            builder: (context, child) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.orange,
                  height: 100,
                  width: 100,
                  transform: Matrix4.translationValues(
                    sequenceAnimation["plane"].value,
                    0.0,
                    0.0,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
额外说明

如果需要调整元素的层级顺序,直接调换Stack中子组件的排列顺序即可,排在后面的子组件会显示在更上层。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 14:57:02