Flutter---动画

概念

从一个状态平滑过渡到另一个状态

4个核心要素

1.AnimationController:动画控制器,管理时间(电影播放器)

2.Animation:动画值,随时间变化(电影画面)

3.Tween:生成器,创建动画范围(剧本:从哪到哪)

4.Curve:动画曲线,控制动画随时间的变化

AnimationController的常见方法

1.forward():开始播放动画

2.stop():停止动画播放

3.reset():重制动画为初始化状态

4.reverse():反向播放动画,必须正向动画播放完成之后才有效

5.repeat():循环播放动画

6.dispose():销毁动画,释放动画占用资源。

Animation

四种状态值

1.dismissed:动画处于开始状态。

2.forward:动画正在正向执行。

3.reverse:动画正在反向执行。

4.completed:动画处于结束状态。

有两个监听器Listeners和StatusListeners

1.addListener():用于给Animation对象添加帧监听器,每一帧都会被调用,当帧监听器监听到状态发生改变后就会调用setState()来触发视图的重建。

2.addStatusListener():用于给Animation对象添加动画状态改变监听器,动画开始、结束、正向或反向时就会调用状态改变的监听器。

Tween

begin和end的使用技巧

Dart 复制代码
Tween<double>(begin: 0, end: 1) 定义的是动画的数值范围:

begin:动画开始时的数值

end:动画结束时的数值


1. 0和1的含义

0:通常表示"无"、"开始"、"最小"、"透明"、"隐藏"

1:通常表示"完整"、"结束"、"最大"、"不透明"、"显示"

2.什么时候用0和1

当组件参数本身就是比例时(如opacity、scale、progress)

当需要与其他值相乘计算时

当需要标准化多个动画时

3.什么时候用具体数值

当有明确的物理单位时(像素、角度、颜色)

当需要精确控制时

当直接对应屏幕坐标或尺寸时

4.技巧

透明度:0(透明) → 1(不透明)
缩放:   0(消失) → 1(原大) → >1(放大)
旋转:   0°(开始) → 360°(一圈)
位置:   0(起点) → 100(终点像素)
进度:   0(0%) → 1(100%)

基础动画类tween

Dart 复制代码
// 数值插值-Tween
Animation<double> sizeAnim = Tween<double>(begin: 0, end: 200)
    .animate(_controller);

// 颜色插值-ColorTween
Animation<Color?> colorAnim = ColorTween(
    begin: Colors.red,
    end: Colors.blue
) .animate(_controller);

// 矩形插值-RectTween
Animation<Rect?> rectAnim = RectTween(
    begin: Rect.zero,
    end: Rect.fromLTRB(0, 0, 100, 100))
    .animate(_controller);

// 边界插值-BorderRadiusTween
Animation<BorderRadius?> radiusAnim = BorderRadiusTween(
    begin: BorderRadius.zero,
    end: BorderRadius.circular(20))
    .animate(_controller);

动画序列类AnimationSequence

Dart 复制代码
    // 创建序列动画:包含3个阶段
    _sequenceAnim = TweenSequence<double>([
      // 第一阶段:从0到100,使用easeIn曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 0, end: 100)
            .chain(CurveTween(curve: Curves.easeIn)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),

      // 第二阶段:从100到200,使用easeOut曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 100, end: 200)
            .chain(CurveTween(curve: Curves.easeOut)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),

      // 第三阶段:从200回到150,使用bounceOut曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 200, end: 150)
            .chain(CurveTween(curve: Curves.bounceOut)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),
    ]).animate(_controller);

组合动画类Interval

Dart 复制代码
Animation<double> delayedAnim = Tween<double>(begin: 0, end: 1)
    .animate(CurvedAnimation(
      parent: _controller,
      curve: Interval(0.5, 1.0),  // 在动画的后半段播放
    ));

物理动画类SpringSimulation

Dart 复制代码
final simulation = SpringSimulation(
  SpringDescription(
    mass: 1,
    stiffness: 100,
    damping: 10,
  ),
  0.0,  // 起始值
  1.0,  // 目标值
  100,  // 初始速度
);
_controller.animateWith(simulation);


1.mass质量:默认值通常是1;(0.1~10.0)

值越大 → 惯性越大,动画越"沉重";

值越小 → 响应越快,动画越"轻快"

2.stiffness刚度:弹簧的硬度/弹性系数;(50~1000)

值越大 → 弹簧越硬,动画越快、弹性越强

值越小 → 弹簧越软,动画越慢、弹性越弱

3.stiffness: 100,

damping阻尼:抵抗运动的阻力;(1~50)

值越大 → 阻力越大,动画越快停止(过阻尼)

值越小 → 阻力越小,动画会更多次反弹(欠阻尼)

合适值 → 刚好能平稳停止(临界阻尼)
Curves类定义的动画曲线

基本曲线

Dart 复制代码
1.linear:匀速动画

2.decelerate:匀减速动画

3.ease:先加速后减速动画

4.easeIn:先快后慢动画

5.easeOut:先慢后快动画

6.easeInOut:先慢,然后加速,最后减速动画

弹性/弹簧效果

Dart 复制代码
Curves.elasticIn    // 弹性进入
Curves.elasticOut   // 弹性退出
Curves.elasticInOut // 弹性进出
Curves.bounceIn     // 弹跳进入
Curves.bounceOut    // 弹跳退出
Curves.bounceInOut  // 弹跳进出

其他特殊效果

Dart 复制代码
Curves.decelerate    // 减速
Curves.slowMiddle    // 中间慢
Curves.backIn        // 后退进入
Curves.backOut       // 后退退出
Curves.backInOut     // 后退进出
Flutter动画的标准流程
Dart 复制代码
1.定义动画控制器和动画值。
2.初始化动画控制器,绑定State和设置动画时间。
3.设置动画值的开始值和结束值,然后通过animate绑定动画控制器。
4.在UI中绑定动画控制器

如果需要需要设置时间区间和弹性效果需要animate绑定CurvedAnimation,
用CurvedAnimation绑定动画控制器和设置时间区间和弹性效果

例子:
_fadeAnimation = Tween<double>(begin: 0, end: 1).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(0.0, 0.3, curve: Curves.easeIn),
       
      ),
    );

//InterVal是一种曲线类型,用于控制动画在指定时间区间内发生,可以让多个动画按顺序或分段执行
 // Interval(begin, end) 中的 begin 和 end 指的是时间比例
//比如:begin=0.3:动画在总时间的30%处开始,,end=0.7:动画在总时间的70%处结束
Flutter动画架构层次
Dart 复制代码
UI组件
    ↑
动画值 (Animation<double>)  //定义具体动画变化(大小,位置,颜色等)
    ↑
动画控制器 (AnimationController)  //控制动画播放,暂停,停止,重置
    ↑
TickerProvider (vsync)   //驱动动画
动画之间的区别
Dart 复制代码
1.显式动画:

需要手动管理 AnimationController

有明确的控制器

需要自己处理动画状态

示例中的 _controller.forward()、_controller.reverse()

2.隐式动画:

自动管理动画

如 AnimatedContainer、AnimatedOpacity

Flutter 自动处理开始、结束、反转

3.简单动画

单个 Tween + 单个属性

4.组合动画

多个动画的组合
一.隐式动画

效果图

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {


  bool _isExpanded = false; //容器缩放
  bool _isRotated = false; //旋转状态
  double _opacity = 1.0; //透明度


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('隐式动画')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [

          // 1. AnimatedContainer
          GestureDetector(
            onTap: () => setState(() => _isExpanded = !_isExpanded), //点击切换状态
            child: AnimatedContainer( //AnimatedContainer - 容器动画
              duration: Duration(milliseconds: 500), //动画时长
              width: _isExpanded ? 300 : 150, //变化范围
              height: _isExpanded ? 200 : 100,
              decoration: BoxDecoration( //容器风格
                color: Colors.blue,
                borderRadius: BorderRadius.circular(_isExpanded ? 30 : 10),
              ),
              child: Center(
                child: Text(
                  '点击缩放',
                  style: TextStyle(color: Colors.white, fontSize: 20),
                ),
              ),
            ),
          ),

          SizedBox(height: 30),

          // 2. TweenAnimationBuilder 旋转
          GestureDetector(
            onTap: () => setState(() => _isRotated = !_isRotated), //点击切换状态
            child: TweenAnimationBuilder<double>( //TweenAnimationBuilder - 自定义值动画
              duration: Duration(milliseconds: 800), //动画时长
              tween: Tween(begin: 0, end: _isRotated ? 360 : 0), //值范围定义:动画的起始和结束值
              curve: Curves.easeInOut,  //动画曲线
              builder: (context, angle, child) { //angle由Flutter框架自动传入
                return Transform.rotate( //旋转变换
                  angle: angle * (3.14159 / 180), //当前角度(弧度)
                  child: Container(
                    width: 100,
                    height: 100,
                    color: Colors.green,
                    child: Center(child: Text('旋转')),
                  ),
                );
              },
            ),
          ),

          SizedBox(height: 30),

          // 3. AnimatedOpacity
          GestureDetector(
            onTap: () => setState(() => _opacity = _opacity == 1.0 ? 0.3 : 1.0),
            child: AnimatedOpacity(  //AnimatedOpacity - 透明度动画
              duration: Duration(milliseconds: 500),
              opacity: _opacity,
              child: Container(
                width: 150,
                height: 100,
                color: Colors.orange,
                child: Center(child: Text('透明度动画')),
              ),
            ),
          ),
        ],
      ),
    );
  }

}
二.显式动画

基础显式动画

效果图

代码示例

Dart 复制代码
import 'dart:async';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {


  late AnimationController _controller; //动画控制器
  late Animation<double> _animation; //动画值
  
  @override
  void initState(){
    super.initState();
  
    //1.创建动画控制器
    _controller = AnimationController(
      vsync: this,// 绑定到 State(通过 SingleTickerProviderStateMixin)
      duration: Duration(seconds: 2),
    );
    
    //2.创建动画值(从0到1)
    _animation = Tween<double>(begin: 0,end: 1).animate(_controller);
    
    //3.监听动画状态变化
    _controller.addStatusListener((status){
      if(status == AnimationStatus.completed){
        print("动画完成");
      }else if(status == AnimationStatus.dismissed){
        print("动画重置");
      }
    });
    
  }

  @override
  void dispose() {
    _controller.dispose();  //释放资源
    super.dispose();
  }

  //===========================开始播放动画=============================
  void _startAnimation(){
    if(_controller.status == AnimationStatus.completed){
      _controller.reverse();//反向播放
    }else{
      _controller.forward();//正向播放
    }
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('基础显式动画')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [

         AnimatedBuilder(
             animation: _animation,
             builder: (context,child){
               return Container(
                 width: 100 + _animation.value * 200, //宽度从100到300
                 height: 100,
                 color: Colors.blue.withOpacity(_animation.value),
                 child: Center(
                   child: Text(
                     '${(_animation.value * 100).toInt()}%',
                     style: TextStyle(color: Colors.white),
                   ),
                 ),
               );
             }
         ),
          SizedBox(height: 30,),

          ElevatedButton(
              onPressed: _startAnimation,
              child: Text("开始动画"),
          ),
        ],
      ),
    );
  }

}

使用Curve控制动画曲线

效果图

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {


  late AnimationController _controller; //动画控制器
  late Animation<double> _animation; //动画值


  final List<String> _title = [ //曲线名称列表
    "linear",
    "easeIn",
    "easeOut",
    "easeInOut",
    "bounceOut",
    "elasticOut"
  ];

  final List<Curve> _curves = [ //动画曲线
    Curves.linear,    // 线性
    Curves.easeIn,    // 缓慢开始,加速结束
    Curves.easeOut,   // 快速开始,缓慢结束
    Curves.easeInOut, // 缓慢开始和结束
    Curves.bounceOut, // 弹跳效果
    Curves.elasticOut,// 弹性效果
  ];

  int _currentCurveIndex = 0;  //当前选中的曲线索引


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

    //初始化控制器
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),
    );

    //初始化动画
    _updateAnimation();
  }

  //动画配置方法
  void _updateAnimation() {
    _animation = CurvedAnimation(
      parent: _controller,
      curve: _curves[_currentCurveIndex], //应用当前选中曲线
    ).drive(Tween<double>(begin: 0, end: 300)); //创建动画值范围
  }

  //切换曲线的方法
  void _changeCurve() {

    setState(() {
      _currentCurveIndex = (_currentCurveIndex + 1) % _curves.length; //循环切换
      _updateAnimation(); //更新动画配置
    });
    _controller.reset(); //重置动画到开始状态
    _controller.forward();//开始播放动画
  }

  @override
  void dispose() {
    _controller.dispose();  //释放资源
    super.dispose();
  }
  

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('动画曲线示例')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            //动画显示部分
            AnimatedBuilder(
              animation: _animation, //监听动画值变化
              builder: (context, child) {
                return Container(
                  width: _animation.value,//宽度随值变化
                  height: 100,
                  color: Colors.pink,
                  child: Center(
                    child: Text(
                      _title[_currentCurveIndex], //显示当前曲线名称
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                );
              },
            ),

            SizedBox(height: 30),

            //切换按钮
            ElevatedButton(
              onPressed: _changeCurve,
              child: Text('切换曲线-- ${_title[_currentCurveIndex]}'),
            ),
          ],
        ),
      ),
    );
  }


}

动画序列

效果图

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {

  late AnimationController _controller;
  late Animation<double> _sequenceAnim;
  late Animation<Color?> _colorAnim;

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

    // 创建动画控制器,时长6秒
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 6),
    );

    // 创建序列动画:包含3个阶段
    _sequenceAnim = TweenSequence<double>([
      // 第一阶段:从0到100,使用easeIn曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 0, end: 100)
            .chain(CurveTween(curve: Curves.easeIn)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),

      // 第二阶段:从100到200,使用easeOut曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 100, end: 200)
            .chain(CurveTween(curve: Curves.easeOut)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),

      // 第三阶段:从200回到150,使用bounceOut曲线
      TweenSequenceItem<double>(
        tween: Tween<double>(begin: 200, end: 150)
            .chain(CurveTween(curve: Curves.bounceOut)),
        weight: 1.0, // 权重1,占总时长的1/3
      ),
    ]).animate(_controller);

    // 创建颜色动画,与序列动画同步
    _colorAnim = ColorTween(
      begin: Colors.blue,
      end: Colors.red,
    ).animate(_controller);

    // 监听动画状态变化
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) { //动画在结束位置
        Future.delayed(const Duration(seconds: 1), () {
          _controller.reverse(); //反向播放
        });
      } else if (status == AnimationStatus.dismissed) { //动画在开始位置
        Future.delayed(const Duration(seconds: 1), () {
          _controller.forward(); //正向播放
        });
      }
    });

    // 开始动画
    _controller.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('TweenSequence动画序列示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [

            // 动画显示的方块
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                return Container(
                  width: _sequenceAnim.value, // 宽度使用序列动画的值
                  height: _sequenceAnim.value, // 高度使用序列动画的值
                  color: _colorAnim.value, // 颜色随进度变化
                  alignment: Alignment.center,
                  child: Text(
                    '${_sequenceAnim.value.toInt()}',
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                );
              },
            ),

            const SizedBox(height: 30),

            // 动画进度显示
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                return Text(
                  '进度: ${(_controller.value * 100).toStringAsFixed(1)}%\n'
                      '当前值: ${_sequenceAnim.value.toStringAsFixed(1)}\n'
                      '阶段: ${_getCurrentStage(_controller.value)}',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontSize: 16),
                );
              },
            ),

            const SizedBox(height: 30),

            // 控制按钮
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    if (_controller.isAnimating) {
                      _controller.stop();
                    } else {
                      _controller.isCompleted
                          ? _controller.reverse()
                          : _controller.forward();
                    }
                  },
                  child: AnimatedBuilder(
                    animation: _controller,
                    builder: (context, child) {
                      return Text(
                        _controller.isAnimating ? '暂停' : '继续',
                      );
                    },
                  ),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: () => _controller.reset(),
                  child: const Text('重置'),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: () => _controller.repeat(),
                  child: const Text('循环播放'),
                ),
              ],
            ),

            const SizedBox(height: 20),

            // 动画描述
            Container(
              padding: const EdgeInsets.all(16),
              margin: const EdgeInsets.symmetric(horizontal: 20),
              decoration: BoxDecoration(
                color: Colors.grey[100],
                borderRadius: BorderRadius.circular(10),
              ),
              child: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    '动画序列说明:',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 16,
                    ),
                  ),
                  SizedBox(height: 8),
                  Text('第1阶段 (0-33%):宽度/高度 0→100,easeIn曲线'),
                  Text('第2阶段 (34-66%):宽度/高度 100→200,easeOut曲线'),
                  Text('第3阶段 (67-100%):宽度/高度 200→150,bounceOut曲线'),
                  SizedBox(height: 8),
                  Text('颜色:蓝色→红色,随总进度线性变化'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  // 获取当前动画阶段
  String _getCurrentStage(double value) {
    if (value < 0.33) return '第1阶段: 0→100 (easeIn)';
    if (value < 0.66) return '第2阶段: 100→200 (easeOut)';
    return '第3阶段: 200→150 (bounceOut)';
  }

}
三.组合动画

基础组合动画

效果图:颜色,大小,都会改变,且伴随旋转

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {

  late AnimationController _controller;
  late Animation<double> _sizeAnimation;
  late Animation<double> _rotationAnimation;
  late Animation<Color?> _colorAnimation;

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

    //初始化动画控制器
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds:2), //动画持续时间
    )..repeat(reverse: true); //无限循环播放

    //大小动画
    _sizeAnimation = Tween<double>(begin:50,end:200).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    );

    //旋转动画
    _rotationAnimation = Tween<double>(begin: 0,end:360).animate(
      CurvedAnimation(parent: _controller, curve: Curves.linear),
    );

    //颜色动画
    _colorAnimation = ColorTween(begin: Colors.red,end:Colors.blue,).animate(_controller);

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("组合动画"),),
      body: Center(
        child: AnimatedBuilder(
            animation: _controller,
            builder: (context,child){
              return Transform.rotate(angle: _rotationAnimation.value * (3.14159 / 180 ),
                child: Container(
                  width: _sizeAnimation.value,
                  height: _sizeAnimation.value,
                  decoration: BoxDecoration(
                    color: _colorAnimation.value,
                    shape: BoxShape.circle,
                  ),
                  child: Center(child: Text('组合动画',style: TextStyle(color: Colors.white),),),
                ),
              );
            }
        ),
      ),
    );
  }
}

交错动画

通过重叠或顺序排列的时间区间来控制多个动画的执行时机。

效果图

交错时间线可视化

复制代码
时间轴: 0% ────── 20% ────── 40% ────── 60% ────── 80% ────── 100%
动画:
淡入   │─────────────│
上滑       │─────────────│
缩放               │─────────────│
旋转                     │─────────────│

效果:
1. 0-20%: 只执行淡入
2. 20-40%:淡入+上滑同时执行
3. 40-60%:上滑+缩放同时执行  
4. 60-80%:缩放+旋转同时执行
5. 80-100%:只执行旋转

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {


  late AnimationController _controller; //controller.value 从 0.0 到 1.0(时间进度)

  // 定义多个动画
  late Animation<double> _fadeAnimation;
  late Animation<double> _scaleAnimation;
  late Animation<Offset> _slideAnimation;

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

    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 1500),
    );

    // 淡入动画(0-500ms)
    _fadeAnimation = Tween<double>(begin: 0, end: 1).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(0.0, 0.3, curve: Curves.easeIn),
      ),
    );

    // 缩放动画(200-700ms)
    _scaleAnimation = Tween<double>(begin: 0.5, end: 1).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(0.2, 0.5, curve: Curves.elasticOut),
      ),
    );

    // 滑动动画(400-1200ms)
    _slideAnimation = Tween<Offset>(
      begin: Offset(0, 1), // 向下一个自身高度 ;优点:无论元素多高,都向下移动一个自身高度
      end: Offset.zero,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(0.4, 0.8, curve: Curves.fastOutSlowIn),
      ),
    );
  }

  void _playAnimation() {
    if (_controller.status == AnimationStatus.completed) {
      _controller.reverse();
    } else {
      _controller.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('交错动画')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SlideTransition(
              position: _slideAnimation,
              child: ScaleTransition(
                scale: _scaleAnimation,
                child: FadeTransition(
                  opacity: _fadeAnimation,
                  child: Container(
                    width: 200,
                    height: 200,
                    color: Colors.green,
                    child: Center(
                      child: Text(
                        '交错动画效果',
                        style: TextStyle(color: Colors.white, fontSize: 20),
                      ),
                    ),
                  ),
                ),
              ),
            ),

            SizedBox(height: 30),

            ElevatedButton(
              onPressed: _playAnimation,
              child: Text('播放动画'),
            ),
          ],
        ),
      ),
    );
  }


}

物理动画:弹簧动画

效果图

代码实例

Dart 复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';


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

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {


  late AnimationController _controller; //controller.value 从 0.0 到 1.0(时间进度)

  SpringDescription _spring = SpringDescription(
      mass: 1,//质量
      stiffness: 100,//刚度
      damping: 2//阻尼
  );

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

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 5),
    )..addListener(() => setState(() {}));
  }

  void _startSpringAnimation(double velocity){
    final simulation = SpringSimulation(
        _spring, //弹簧参数
        _controller.value, //起始值
        1.0, //目标值
        velocity //初始速度
    );

    _controller.animateWith(simulation);
  }

  void _reset(){
    _controller.reset();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('物理动画')),
      body: GestureDetector(
        onTap: (){
          _startSpringAnimation(1000);
        },
        onDoubleTap: _reset,
        child: Center(
          child: Transform.translate(
              offset: Offset(0, _controller.value * 200),
            child: Container(
              width: 100,
              height: 100,
              decoration: BoxDecoration(
                color: Colors.red,
                borderRadius: BorderRadius.circular(20)
              ),
              child: Center(
                child: Text(
                  '弹簧动画\n单击开始\n双击重置',
                  textAlign: TextAlign.center,
                  style: TextStyle(color: Colors.white),

                ),
              ),
            ),
          ),
        ),
      )
    );
  }


}
相关推荐
码农汉子1 天前
构建属于自己的Flutter混合开发框架
flutter·dart
xkxnq1 天前
第一阶段:Vue 基础入门(第 8 天)
前端·vue.js·flutter
kirk_wang1 天前
Flutter flutter_pdfview 在 OpenHarmony 平台的适配实战:原理与实现指南
flutter·移动开发·跨平台·arkts·鸿蒙
西西学代码2 天前
Flutter---CustomPaint
学习·flutter
火柴就是我2 天前
flutter 实现文本贴图
flutter
Swift社区2 天前
Flutter / RN / iOS 的 UI 渲染机制,本质差异在哪里?
flutter·ui·ios
小蜜蜂嗡嗡2 天前
flutter可吸边且隐藏一半的拖拽悬浮按钮
flutter
AiFlutter2 天前
三、内容展示(04):条形码
flutter·低代码平台·aiflutter·aiflutter低代码·flutter低代码开发