Flutter动画初探

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chunqiuwei/article/details/86585358

本篇算是一篇学习笔记,主要对《Flutter实战》这本书上关于动画的章节做了一个简单的梳理,并且对上面的demo也做了统一整理,本篇博文的demo源码可以点击此处下载.运行页面如下:
在这里插入图片描述
这个demo程序列举了Tween,ColorTween,ReverseTween,AnimatedWidget等有关动画的的基础应用。动画效果读者可以下载源码运行。


本篇就以上述demo中Tween的动画为例来分析下Flutter的相关知识。该例子实现了一张图片的从小到大然后再从大到小的循环缩放。先来看看具体的代码:
在这里插入图片描述
如上图所示为了实现图片循环缩放的效果需要如下三个对象:
1、一个动画对象Animation
2、一个Tween对象,该对象用来控制图片变化的值,比如本例中让图片大小从0放大到300.
3、一个AnimationController对象,用来控制动画,比如控制动画的时间,开始和结束,比如本例中就是让缩放动画3秒中之内把图片的大小从0变成300。

通过controller.forward()方法启动动画后,通过动画的监听对象里调用 setState(() => {});就可以告知Flutter对页面重新build.然后将当前时间[0,300]区间的某个value设置给图片,这个值我们可以通过animation.value拿到,这个value值得是动画在此时此刻的某一值,代码如下所示:

Image.asset(
            "images/beauty1.jpg",
            width: animation.value,//图片的宽
            height: animation.value//图片的高
            )

因为是循环动画,所以需要知道动画的状态,我们可以通过两种方法拿到。第一种如下:

 animation.addListener(() {
     setState(() => {});
   if(animation.isCompleted){
       controller.reverse();//放大过后缩小
    }else if(animation.isDismissed){
       controller.forward();//继续下一轮
     }
 });

除了addListener之外,Flutter也提供了状态监听对象,也即是第二种:


 animation.addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          controller.reverse();//从大到小,然后从小到大循环
        } else if (status == AnimationStatus.dismissed) {
          controller.forward();
        }
      });

从上面的例子可以知道Animation拥有动画的当前值value和状态status两个值,我们可以将此value设置给一个Widget的某个属性,比如本例子的宽高。这个value是泛型的,可以是数字,也可以是其他值。比如颜色值(参考例子源码中的ColorTween的 简单使用)等等。其实也可以看出Animation和UI是无关的。这点倒是跟Android的属性动画有些类似,因为属性动画的对象也不仅仅局限于Android某个具体的View对象,可以是其他对象(关于属性动画的原理可参考博主此类博客

之所以能实现上述动画的循环播放,是因为我们在监听了动画的状态,在动画结束的时候也就是status=completed执行reverse()方法。然后reverse方法执行结束后后status==dimissed状态。继续forward。这样就完成了图片从小到大再从大到小的播放过程。那么Flutter动画都的状态都有什么意思呢?

状态 含义
dismissed 动画在开始点停止或者说是恢复初始状态
forward 动画正在正向执行
reverse 动画正在方向执行
completed 动态在终点停止

其中dismissed和completed都表明动画已经停止,但是一个是正向停止,一个是反向停止或者说是恢复了初始状态。


AnimatedWidget的简单使用

实现图片放大缩小核心是拿到Animation.value值赋值给Widget的宽和高,但是实现widget刷新的代码却是如下这一句:

//动画监听
animation.addListener(() {
   //告知
     setState(() => {});
 });

但是如果每次使用动画的时候都要加上这段代码,未免显得太不优雅.所以Flutter提供了AnimatedWidget这个组件。代码改动如下:
在这里插入图片描述
从代码中可以看到,我们直接把前文初始化好的animation对象传给上图中的_AnimatedWidget对象即可。不需要额外的在添加addListener这段代码了。同样的可以实现图片放大缩小的效果。为什么呢?有兴趣的话可以看看AnimatedWidget的源码,原理很简单,这里就大致说一下:
在这里插入图片描述
如图所示,AnimatedWidget有如下特点:
1)持有了一个Listenable引用,而Animation继承了Listenable
2)在_AnimatedState的inState()方法中自动添加了Listener(上图_handleChange方法)
3)AnimatedWidget方法定义了一个抽象方法build,在动画执行的时候通过handleChange执行setState()方法引起重绘,自动回调了AnimatedWidget的build方法。
4)注意AnimatedWidget是一个Stateful的Widget

但是直接使用AnimatedWidget这有一个问题:就是引起了不必要的重绘。比如下面代码:
在这里插入图片描述
我们只需要重绘Image这个对象,但是因为AnimatedWidget的机制:随着动画的执行会不断调用build方法,那么上图中黄色矩形框里的对象也都需要重新创建,而这个创建是不必要的。如果放在更复杂的布局里面性能就更不高了。那么有什么可以改变这种不太优雅的方式吗?AnimatedBuilder粉末登场。


AnimatedBuilder的简单使用

先来看看AnimatedBuilder是神马玩意:
在这里插入图片描述
可以看出AnimatedBuilder直接继承了AnimatedWidget,且扩展了一个child对象,这个对象正是我们需要使用动画的对象。另外AnimatedBuilder重写了父类的build方法,见上图。

所以高效率的图片放大缩小的动画,如果使用AnimatedBuilder的话,就是如下所示:
在这里插入图片描述

到此为止,Flutter动画的简单使用讲解完毕。还有很多复杂的动画逻辑,因为博主还没来研究,就不再多说。如有不当之处,欢迎批评指正。

最后再耗费高级灵石开启一次本篇博文的源代码传送门。感兴趣的可以下载下来运行一下看看具体动画效果。

展开阅读全文

没有更多推荐了,返回首页