老司机快来!2分钟搞懂flutter的动画:)

251 阅读4分钟

flutter中的动画千变万化,但是始终不过2种:‘隐式动画’与‘显示动画’。

什么是隐式动画?

不需要手动控制的动画。所谓的隐式就是指控制动画的过程由系统帮你完成了。可以给它比作太阳,它天生就能动(其实底层也是通过显示动画那套实现的)。 它的特点是:

  1. 自动控制;
  2. 简单(2句代码就可以实现动画);
  3. 典型代表:AnimatedContainerAnimatedOpacity 等以 Animated 开头的组件

上代码:

AnimatedSize( //第一句。
  duration: Duration(seconds: 1), // 第二句。
  child: Container(
    width: 100, // 首次运行后改动这个值为200,然后按保存按钮,就会看见动画
    height: 100,// 首次运行后改动这个值为200,然后按保存按钮,就会看见动画
    color: Colors.blue,
  ),
)

当然,实际中肯定不是按保存按钮执行动画!不过其实也差别不大,只是你自己用代码改变child的大小而已,这个动画就会自己动起来。(由于太过简单,不需要再多解释了)

什么是显示动画?

需要手动控制动画。所谓的显示就是指需要自己搞一个AnimationController来控制动画。可以给它比作一个汽车,需要引擎来让轮子转起来。 它的特点是:

  1. 使用 AnimationController 显式控制;
  2. 更灵活,但需要自己管理动画生命周期;
  3. 典型代表:AnimatedBuilderAnimatedWidget

上代码:

class _MyAnimState extends State<MyAnim> with SingleTickerProviderStateMixin {
    late AnimationController _controller;
    late Animation<double> _animation;

    @override
    void initState() {
      super.initState();
      // 创建动画控制器
      _controller = AnimationController(
        duration: Duration(seconds: 1),
        vsync: this,
      );

      // 创建动画
      _animation = Tween<double>(
        begin: 0,
        end: 200,
      ).animate(_controller);
    }

    @override
    Widget build(BuildContext context) {
      return Column(
        children: [
          AnimatedBuilder(
            animation: _animation,
            builder: (context, child) {
              return Container(
                width: _animation.value,
                height: _animation.value,
                color: Colors.blue,
              );
            },
          ),
          ElevatedButton(
            onPressed: () {
              // 手动控制动画
              if (_controller.status == AnimationStatus.completed) {
                _controller.reverse();
              } else {
                _controller.forward();
              }
            },
            child: Text('触发动画'),
          ),
        ],
      );
    }
}

代码里的with SingleTickerProviderStateMixin是什么呢?

还是用前面提到的汽车来举例:我们知道,系统是一直在运行的,它本身就相当于一个引擎。当我们with上这个SingleTickerProviderStateMixin之后,就相当于给汽车挂了个档(开机就是打火,引擎其实早就启动了),相当于告诉操作系统:你运行的动力我们也需要用到了,也可以说是给操作系统的引擎运动函数加了个回调而已。

所以,我们的方向盘就是AnimationController(注:vsync就是连接了SingleTickerProviderStateMixin),我们现在有了AnimationController可以控制动画了,它本身也有驱动的动力了。

现在我们有了第一个需求:启动

controller.forward(); // 启动
//顺便学会其他几个
controller.reverse(); // 倒车 
controller.stop(); // 停车 
controller.reset(); // 前面不算,重来

光会前进是不够的,我们还要会:转弯与加减速

// 添加加速减速的效果。比真实开车更强的是它的加减速有好多预设模式:Curves.bounceOut、Curves.slowMiddle等;
final curvedAnimation = CurvedAnimation( parent: controller, curve: Curves.bounceOut);

比如Curves.bounceOut的样子是这样: out.gif

我们知道,挂上1档5档的车速其实是不一样的,那在flutter里是怎么换档的?

那就是Tween

// 连接上前面的controller
// 当 controller.value 是 0.0 时,输出 0 
// 当 controller.value 是 1.0 时,输出 200 
Tween<double> tween = Tween<double>( begin: 0, end: 200);

其实到这里也说得差不多了,最终的动画是通过AnimatedBuilder之类的应用到实际页面上面的。

现在就剩最后一个问题:这4个有什么关系?

我们可以通过下面这个图来解释一下: 通过Controller提供动力和操控(Controller的动力来源是它与SingleTickerProviderStateMixin连接起来的),通过Curve来调节方向与速度,通过Tween来换档,最后通过Animation把最终的效果应用到实际以达到页面按需动起来的目的。 iShot_2025-02-21_16.03.15.png

补充:

  1. ControllerlowerBoundupperBound2个构造参数来把范围作一下调整,这样简单的话(简单是指单个动画,其实还有多个动画串联和并联)可以把Tween给省略掉(直接使用controller.value);
  2. Tween不仅仅可以把Controller的数字转换为数字,还可以是颜色等;
  3. 预设的模式真的很多,还有SawToothInterval等等各式花哨的Curve,实在不满足就自己自定义也行。