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

Flutter连续动画实现:第二个动画从首个动画终点启动的方法

Flutter实现连续的衔接式动画(从上个动画结束值开始)

方案一:使用TweenSequence实现(一次性定义连续动画)

如果你之前用TweenSequence没成功,大概率是没给每个阶段的补间正确绑定曲线,或者没做好值的衔接。以下是正确的实现方式:

代码示例

import 'package:flutter/material.dart';

class CarSpeedAnimation extends StatefulWidget {
  const CarSpeedAnimation({super.key});

  @override
  State<CarSpeedAnimation> createState() => _CarSpeedAnimationState();
}

class _CarSpeedAnimationState extends State<CarSpeedAnimation> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _speedAnimation;

  @override
  void initState() {
    super.initState();
    // 总动画时长:前2秒完成0→50,后2秒完成50→70
    _controller = AnimationController(
      duration: const Duration(seconds: 4),
      vsync: this,
    );

    _speedAnimation = TweenSequence<double>([
      // 第一阶段:0→50,使用Curves.easeIn曲线
      TweenSequenceItem(
        tween: Tween<double>(begin: 0, end: 50).chain(CurveTween(curve: Curves.easeIn)),
        weight: 1.0, // 权重决定占总时长的比例,和下一个1.0平分总时长
      ),
      // 第二阶段:50→70,同样使用Curves.easeIn曲线
      TweenSequenceItem(
        tween: Tween<double>(begin: 50, end: 70).chain(CurveTween(curve: Curves.easeIn)),
        weight: 1.0,
      ),
    ]).animate(_controller);

    // 启动动画
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: AnimatedBuilder(
          animation: _speedAnimation,
          builder: (context, child) {
            return Text(
              '当前速度:${_speedAnimation.value.toStringAsFixed(1)} mph',
              style: const TextStyle(fontSize: 24),
            );
          },
        ),
      ),
    );
  }
}

关键说明

  • 每个TweenSequenceItem的tween需要通过chain(CurveTween(curve: Curves.easeIn))单独绑定曲线,确保每个阶段都使用easeIn动画曲线。
  • weight参数控制各阶段动画的时长占比,比如两个阶段都设为1.0,就会平分总时长。
  • 两个阶段的begin和end要严格衔接,第一阶段的end等于第二阶段的begin,保证动画平滑过渡。

方案二:分阶段触发动画(更灵活的控制方式)

如果需要在两个动画之间加入延迟、用户交互等逻辑,分阶段触发的方式会更合适。可以用一个AnimationController来实现:

代码示例

import 'package:flutter/material.dart';

class CarSpeedAnimation extends StatefulWidget {
  const CarSpeedAnimation({super.key});

  @override
  State<CarSpeedAnimation> createState() => _CarSpeedAnimationState();
}

class _CarSpeedAnimationState extends State<CarSpeedAnimation> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _speedAnimation;
  double _currentSpeed = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    // 初始化第一个动画:0→50
    startFirstAnimation();
    _controller.forward();
  }

  void startFirstAnimation() {
    _speedAnimation = Tween<double>(begin: 0, end: 50)
        .chain(CurveTween(curve: Curves.easeIn))
        .animate(_controller)
      ..addListener(() {
        setState(() {
          _currentSpeed = _speedAnimation.value;
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          // 第一个动画完成后,启动第二个动画:50→70
          _controller.reset();
          startSecondAnimation();
          _controller.forward();
        }
      });
  }

  void startSecondAnimation() {
    _speedAnimation = Tween<double>(begin: 50, end: 70)
        .chain(CurveTween(curve: Curves.easeIn))
        .animate(_controller)
      ..addListener(() {
        setState(() {
          _currentSpeed = _speedAnimation.value;
        });
      });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(
          '当前速度:${_currentSpeed.toStringAsFixed(1)} mph',
          style: const TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

关键说明

  • 通过AnimationStatus.completed监听第一个动画的完成状态,触发第二个动画。
  • 重置Controller后,重新创建从50到70的补间动画,保证第二个动画从第一个的结束值开始。
  • 如果需要在两个动画之间加入延迟,可以在触发第二个动画前加入Future.delayed(const Duration(seconds: 1))。

方案三:双Controller版本(更清晰的状态分离)

如果不想复用Controller,也可以用两个独立的Controller分别控制两个动画,状态更清晰:

import 'package:flutter/material.dart';

class CarSpeedAnimation extends StatefulWidget {
  const CarSpeedAnimation({super.key});

  @override
  State<CarSpeedAnimation> createState() => _CarSpeedAnimationState();
}

class _CarSpeedAnimationState extends State<CarSpeedAnimation> with SingleTickerProviderStateMixin {
  late AnimationController _controller1;
  late AnimationController _controller2;
  late Animation<double> _speedAnimation1;
  late Animation<double> _speedAnimation2;
  double _currentSpeed = 0;

  @override
  void initState() {
    super.initState();
    _controller1 = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );
    _controller2 = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );

    // 第一个动画:0→50
    _speedAnimation1 = Tween<double>(begin: 0, end: 50)
        .chain(CurveTween(curve: Curves.easeIn))
        .animate(_controller1)
      ..addListener(() {
        setState(() {
          _currentSpeed = _speedAnimation1.value;
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          // 第一个动画完成,启动第二个动画
          _controller2.forward();
        }
      });

    // 第二个动画:50→70
    _speedAnimation2 = Tween<double>(begin: 50, end: 70)
        .chain(CurveTween(curve: Curves.easeIn))
        .animate(_controller2)
      ..addListener(() {
        setState(() {
          _currentSpeed = _speedAnimation2.value;
        });
      });

    // 启动第一个动画
    _controller1.forward();
  }

  @override
  void dispose() {
    _controller1.dispose();
    _controller2.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(
          '当前速度:${_currentSpeed.toStringAsFixed(1)} mph',
          style: const TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

关键说明

  • 两个Controller独立控制各自的动画,避免复用Controller可能带来的状态混乱。
  • 同样通过第一个动画的完成状态监听来触发第二个动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:10:27