一文了解 Flutter 动画

动画,顾名思义就是让页面元素动起来

介绍 AnimationController,Tween,Transform,AnimationBuilder

AnimationController 动画控制,本质上看 是设置了变化区间为0.0到1.0的 Animation
Tween 专注于动画值的计算,用法是将AnimationController的变化范围映射为具体的效果范围,例如尺寸动画,颜色动画等
dart 复制代码
    // 1. 尺寸动画:50 -> 200
    _sizeAnimation = Tween<double>(begin: 50, end: 200).animate(_controller);
    
    // 2. 颜色动画:蓝色 -> 红色
    _colorAnimation = ColorTween(
      begin: Colors.blue,
      end: Colors.red,
    ).animate(_controller);
    
    // 3. 边框动画:方形 -> 圆形
    _borderAnimation = BorderRadiusTween(
      begin: BorderRadius.circular(0),
      end: BorderRadius.circular(100),
    ).animate(_controller);
    
    // 4. 位置动画:从左上到右下
    _offsetAnimation = Tween<Offset>(
      begin: const Offset(-100, -100),
      end: const Offset(100, 100),
    ).animate(_controller);

使用 Curve 和 CurvedAnimation 设置动画曲线

dart 复制代码
_controller 
= AnimationController(
      vsync
: this,
      duration
: const Duration(seconds: 3),
    )..repeat(reverse: true);
    
    // 使用不同的曲线
    _linearAnimation 
= Tween(begin: 0.0, end: 300.0).animate(
      CurvedAnimation(
        parent
: _controller,
        curve
: Curves.linear,
      ),
    );

Transform 用于翻转,缩放,旋转 Widget

dart 复制代码
                AnimatedBuilder(
                  animation: _transformAnimation,
                  builder: (context, child) {
                    return Transform.translate(
                      offset: Offset(_transformAnimation.value * 100, 0),
                      child: _buildBox('平移', Colors.blue),
                    );
                  },
                ),
                // 2. 旋转变换
                AnimatedBuilder(
                  animation: _transformAnimation,
                  builder: (context, child) {
                    return Transform.rotate(
                      angle: _transformAnimation.value * 3.14159, // 0-π 弧度
                      child: _buildBox('旋转', Colors.green),
                    );
                  },
                ),
                // 3. 缩放变换

                AnimatedBuilder(
                  animation: _transformAnimation,
                  builder: (context, child) {
                    return Transform.scale(
                      scale: 0.5 + _transformAnimation.value * 0.5, // 0.5-1.0
                      child: _buildBox('缩放', Colors.red),
                    );
                  },
                ),
                // 4. 3D 变换(沿 Y 轴旋转)
                AnimatedBuilder(
                  animation: _transformAnimation,
                  builder: (context, child) {
                    return Transform(
                      transform: Matrix4.identity()
                        ..setEntry(3, 2, 0.001) // 透视
                        ..rotateY(_transformAnimation.value * 3.14159),
                      alignment: Alignment.center,
                      child: _buildBox('3D旋转', Colors.purple),
                    );
                  },
                ),

隐式动画及自定义动画介绍

隐式动画

一般用于:简单的动画交互展示,比如按钮从不可点击到可点击,从方变圆. 为动画渐变效果

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

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

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

class _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {
  double _padding = 10;
  var _align = Alignment.topRight;
  double _height = 100;
  double _left = 0;
  Color _color = Colors.red;
  TextStyle _style = const TextStyle(color: Colors.black);
  Color _decorationColor = Colors.blue;
  double _opacity = 1;

  @override
  Widget build(BuildContext context) {
    var duration = const Duration(milliseconds: 400);
    return SingleChildScrollView(
      child: Column(
        children: <Widget>[
          ElevatedButton(
            onPressed: () {
              setState(() {
                _padding = 20;
              });
            },
            child: AnimatedPadding(
              duration: duration,
              padding: EdgeInsets.all(_padding),
              child: const Text("AnimatedPadding"),
            ),
          ),
          SizedBox(
            height: 50,
            child: Stack(
              children: <Widget>[
                AnimatedPositioned(
                  duration: duration,
                  left: _left,
                  child: ElevatedButton(
                    onPressed: () {
                      setState(() {
                        _left = 100;
                      });
                    },
                    child: const Text("AnimatedPositioned"),
                  ),
                )
              ],
            ),
          ),
          Container(
            height: 100,
            color: Colors.grey,
            child: AnimatedAlign(
              duration: duration,
              alignment: _align,
              child: ElevatedButton(
                onPressed: () {
                  setState(() {
                    _align = Alignment.center;
                  });
                },
                child: const Text("AnimatedAlign"),
              ),
            ),
          ),
          AnimatedContainer(
            duration: duration,
            height: _height,
            color: _color,
            child: TextButton(
              onPressed: () {
                setState(() {
                  _height = 150;
                  _color = Colors.blue;
                });
              },
              child: const Text(
                "AnimatedContainer",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          AnimatedDefaultTextStyle(
            child: GestureDetector(
              child: const Text("hello world"),
              onTap: () {
                setState(() {
                  _style = const TextStyle(
                    color: Colors.blue,
                    decorationStyle: TextDecorationStyle.solid,
                    decorationColor: Colors.blue,
                  );
                });
              },
            ),
            style: _style,
            duration: duration,
          ),
          AnimatedOpacity(
            opacity: _opacity,
            duration: duration,
            child: TextButton(
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.blue)),
              onPressed: () {
                setState(() {
                  _opacity = 0.2;
                });
              },
              child: const Text(
                "AnimatedOpacity",
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          // AnimatedDecoratedBox1(
          //   duration: Duration(
          //       milliseconds: _decorationColor == Colors.red ? 400 : 2000),
          //   decoration: BoxDecoration(color: _decorationColor),
          //   child: Builder(builder: (context) {
          //     return TextButton(
          //       onPressed: () {
          //         setState(() {
          //           _decorationColor = _decorationColor == Colors.blue
          //               ? Colors.red
          //               : Colors.blue;
          //         });
          //       },
          //       child: const Text(
          //         "AnimatedDecoratedBox toggle",
          //         style: TextStyle(color: Colors.white),
          //       ),
          //     );
          //   }),
          // )
        ].map((e) {
          return Padding(
            padding: const EdgeInsets.symmetric(vertical: 16),
            child: e,
          );
        }).toList(),
      ),
    );
  }
}
性能优化
1. 使用AnimatedBuilder优化性能

使用AnimatedBuilder 代替setState()({}), 用部分重绘代替全页面重绘

2. 使用TweenAnimationBuilder简化代码
dart 复制代码
TweenAnimationBuilder<Color?>(
            duration: Duration(milliseconds: 2000),
            tween: ColorTween(
              begin: Colors.red,
              end: Colors.blue,
            ),
            builder: (context, color, child) {
              return Container(
                width: 100,
                height: 100,
                color: color,
                child: child,
              );
            },
            child: Icon(Icons.star, color: Colors.white),
          )
自定义动画

总结

1.AnimationController 是大脑:控制时间线,生成0.0-1.0的基础值

作用:控制动画的播放,包括开始、停止、前进、后退等。它生成一个介于0.0到1.0之间的值(默认),可以指定动画的时长、重复次数等。

关系:它是动画的"驱动器",是Animation的子类,因此本身就是一个动画(输出0.0到1.0的线性值)。它通常与Tween配合,将0.0到1.0映射到其他值(如颜色、大小等)。

2.Tween 是翻译官:将基础值翻译成具体的应用值(颜色、大小等)

作用:定义动画的起始值和结束值,并定义了如何在这两个值之间进行插值(线性或非线性)。例如,Tween可以定义从10.0到200.0的补间,ColorTween可以定义从红色到蓝色的补间。

关系:Tween本身并不是动画,它需要结合Animation(比如AnimationController)才能生成具体的值。通过animate方法传入一个动画(比如AnimationController),返回一个Animation对象。

3.Animation 是数据流:承载具体的动画数值,通知监听器

作用:是一个值的容器,这个值在动画的每一帧中都会改变。它知道当前动画的值和状态(比如是否完成),但不知道如何渲染。

关系:Tween.animate(AnimationController) 返回一个Animation对象,这个对象在动画过程中会输出从Tween.begin到Tween.end的值。

4.AnimatedBuilder 是桥梁:连接动画数据和UI构建

作用:是一个Widget,它监听Animation对象,当动画值改变时,会重新调用builder方法重建子Widget树。它能够将动画与Widget构建分离,提高性能。

关系:它需要传入一个Animation对象和一个builder函数。在builder函数中,我们可以根据动画的当前值来构建Widget。

5.Transform/AnimatedContainer 是执行者:将动画值应用到UI变化上
Transform

作用:是一个Widget,可以对子Widget进行矩阵变换(如平移、旋转、缩放等)。

关系:在动画中,我们经常使用Transform来根据动画值改变Widget的位置、大小或旋转角度。

AnimatedContainer

作用:是一个特殊的Container,当它的属性(如宽度、高度、颜色等)改变时,会自动以动画方式过渡到新值。它内部实际上封装了AnimationController、Tween和AnimatedBuilder等。

关系:它简化了动画的创建,只需要设置新的属性值,动画就会自动执行。适用于简单的属性动画。

相关推荐
音浪豆豆_Rachel2 小时前
Flutter鸿蒙化之深入解析Pigeon基本数据类型:primitive.dart全解
flutter·华为·harmonyos
小蜜蜂嗡嗡2 小时前
flutter PageView:竖直方向滑动,并且在屏幕中提前显示出下一页的四分之一
flutter
翻斗花园岭第一爆破手2 小时前
flutter学习1
学习·flutter
kirk_wang2 小时前
鸿蒙与Flutter移动开发
flutter·移动开发·跨平台·arkts·鸿蒙
搬砖的kk3 小时前
Flutter适配鸿蒙:跨平台力量为鸿蒙生态注入增长新动能
分布式·flutter·harmonyos
西西学代码3 小时前
Flutter---轮播图
flutter
武玄天宗3 小时前
第五章、flutter怎么创建底部底部导航栏界面
前端·flutter
子榆.3 小时前
Flutter 与开源鸿蒙(OpenHarmony)生物识别实战:人脸 + 指纹双模认证,筑牢信创应用安全防线
flutter·开源·harmonyos
子榆.4 小时前
Flutter 与开源鸿蒙(OpenHarmony)离线地图与定位实战:无网络也能精准导航
flutter·开源·harmonyos