【Flutter 实战】一文学会20多个动画组件

老孟导读:此篇文章是 Flutter 动画系列文章第三篇,后续还有动画序列、过度动画、转场动画、自定义动画等。
Flutter 系统提供了20多个动画组件,只要你把前面【动画核心】(文末有链接)的文章看明白了,这些组件对你来说是非常轻松的,这些组件大部分都是对常用操作的封装。
显示动画组件
回顾上一篇【动画核心】的文章中创建动画三个必须的步骤:
- 创建 AnimationController。
- 监听 AnimationController,调用
setState刷新UI。 - 释放 AnimationController。
看第二步,每个动画都需要这个步骤,因此对其封装,命名为 MyAnimatedWidget:
class MyAnimatedWidget extends StatefulWidget {
final AnimationController controller;
final Widget child;
const MyAnimatedWidget(
{Key key, @required this.controller, @required this.child})
: super(key: key);
@override
_MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> {
@override
void initState() {
super.initState();
widget.controller.addListener(() {
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void dispose() {
super.dispose();
widget.controller.dispose();
}
}
自定义的动画组件只有两个功能:
- 监听 AnimationController,调用
setState。 - 释放 AnimationController。
而 AnimationController 的创建需要开发者自行创建,为什么封装在自定义组件内?这个后面会介绍。
其实这个组件不用我们自己封装,因为系统已经封装好了,在学习 Flutter 的过程中自定义组件是非常重要的,因此多封装一些组件,即使是系统已经存在的,用自己和系统的进行对比,可以极大的提高我们自定义组件的能力。
系统封装的类似上面的组件是 AnimatedWidget,此类是抽象类,源代码:

区别:
我们使用 监听 AnimationController,调用
setState,而系统使用 Listenable,Listenable 是一个维护侦听器列表的对象,用于通知客户端该对象已被更新。Listenable 有两个变体:
- ValueListenable :扩展[Listenable]接口的接口,具有当前值的概念。
- Animation:一个扩展[ValueListenable]接口的接口,添加方向(正向或反向)的概念。
AnimationController 的继承结构:

AnimationController 也是继承自 Listenable,因此使用 Listenable 适用的范围更广,不仅仅可以用于 Animation ,还可以用于 ChangeNotifier。
由于使用了 Listenable,因此监听和释放使用
listenable.addListener和listenable.removeListener。
AnimatedWidget 是一个抽象类,不能直接使用,其子类包括:

以 ScaleTransition 为例使用方式:
class AnimationDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AnimationDemo();
}
class _AnimationDemo extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
@override
void initState() {
_animationController =
AnimationController(duration: Duration(seconds: 2), vsync: this);
_animation = Tween(begin: .5, end: .1).animate(_animationController);
//开始动画
_animationController.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _animation,
child: Container(
height: 200,
width: 200,
color: Colors.red,
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}

和【动画核心】中写法唯一的不同是不需要主动调用 setState。
AnimatedWidget 其他子类的用法类似,不在一一介绍,其他组件的详细用法可到 http://laomengit.com/flutter/widgets/widgets_structure.html 中查看。
隐式动画组件
AnimatedWidget 只是封装了 setState,系统是否有封装 AnimationController、Tween、Curve且自动管理AnimationController的组件呢?有的,此组件就是 ImplicitlyAnimatedWidget,ImplicitlyAnimatedWidget 也是一个抽象类,其子类包括:

以 AnimatedOpacity 为例使用方式:
class AnimatedWidgetDemo extends StatefulWidget {
@override
_AnimatedWidgetDemoState createState() => _AnimatedWidgetDemoState();
}
class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo> {
double _opacity = 1.0;
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedOpacity(
opacity: _opacity,
duration: Duration(seconds: 2),
child: GestureDetector(
onTap: () {
setState(() {
_opacity = 0;
});
},
child: Container(
height: 60,
width: 150,
color: Colors.blue,
),
),
),
);
}
}

使用 AnimatedOpacity 我们并没有主动创建 AnimationController 和 Tween,是因为 AnimatedOpacity 内部已经创建了。
所以别看 Flutter 内置了20多种动画组件,90% 都是对上面两种方式的封装,分别称为隐式动画组件 和 显示动画组件:
- 隐式动画组件:只需提供给组件动画开始、结束值,组件创建 AnimationController、Curve、Tween,执行动画,释放AnimationController,我们称之为隐式动画组件,隐式动画组件有: AnimatedAlign、AnimatedContainer、AnimatedDefaultTextStyle、AnimatedOpacity、AnimatedPadding、AnimatedPhysicalModel、AnimatedPositioned、AnimatedPositionedDirectional、AnimatedTheme、SliverAnimatedOpacity、TweenAnimationBuilder、AnimatedContainer 等。
- 显示动画组件:需要设置 AnimationController,控制动画的执行,使用显式动画可以完成任何隐式动画的效果,甚至功能更丰富一些,不过你需要管理该动画的 AnimationController 生命周期,AnimationController 并不是一个控件,所以需要将其放在 stateful 控件中。显示动画组件有:AlignTransition、AnimatedBuilder、AnimatedModalBarrier、DecoratedBoxTransition、DefaultTextStyleTransition、PositionedTransition、RelativePositionedTransition、RotationTransition、ScaleTransition、SizeTransition、SlideTransition 、FadeTransition 等。
不难看出,使用隐式动画控件,代码更简单,而且无需管理 AnimationController 的生命周期,有人觉得隐式动画组件多方便啊,为什么还要显示动画组件呢?因为:封装的越复杂,使用越简单,往往伴随着功能越不丰富。比如想让动画一直重复执行,隐式动画组件是无法实现的。
显示动画组件和隐式动画组件中各有一个万能的组件,它们是 AnimatedBuilder 和 TweenAnimationBuilder,当系统中不存在我们想要的动画组件时,可以使用这两个组件,以 AnimatedBuilder 为例,实现 Container 大小和颜色同时动画,
class AnimatedBuilderDemo extends StatefulWidget {
@override
_AnimatedBuilderDemoState createState() => _AnimatedBuilderDemoState();
}
class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Color> _colorAnimation;
Animation<Size> _sizeAnimation;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 2));
_colorAnimation =
ColorTween(begin: Colors.blue, end: Colors.red).animate(_controller);
_sizeAnimation =
SizeTween(begin: Size(100.0, 50.0), end: Size(200.0, 100.0))
.animate(_controller);
_controller.forward();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, widget) {
return Container(
width: _sizeAnimation.value.width,
height: _sizeAnimation.value.height,
color: _colorAnimation.value,
);
},
),
);
}
}

AnimatedBuilder 和 TweenAnimationBuilder 本质上和其他动画组件没有区别,只是给了我们更高的灵活性。
如何选取
Flutter 内置的动画组件分为两种:隐式动画组件 和 显示动画组件 ,显示动画组件只封装了 setState 方法,需要开发者创建 AnimationController,并管理 AnimationController。隐式动画组件封装了 AnimationController、Curve、Tween,只需提供给组件动画开始、结束值,其余由系统管理。
隐式动画组件可以完成效果,显示动画组件都可以完成,那么什么时候使用隐式动画组件?什么时候使用显示动画组件?
- 判断你的动画组件是否一直重复,比如一直转圈的loading动画,如果是选择显式动画。
- 判断你的动画组件是否需要多个组件联动,如果是选择显式动画。
- 判断你的动画组件是否需要组合动画,如果是选择显式动画。
- 如果上面三个条件都是否,就选择隐式动画组件,判断是否已经内置动画组件,如果没有,使用 TweenAnimationBuilder,有就直接使用内置动画组件。
- 选择显式动画组件,判断是否已经内置动画组件,如果没有,使用 AnimatedBuilder,有就直接使用内置动画组件。
逻辑图如下:

还有一个简单的区分办法:如果你的动画相对比较简单,动画从一种状态过渡到另一种状态,不需要单独控制 AnimationController,这种情况下,隐式动画组件一般可以就可以实现。
不过也没有必要特别纠结使用隐式动画组件还是显示动画组件,不管使用哪一种,实现效果即可。
交流
老孟Flutter博客地址(330个控件用法):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |
【Flutter 实战】一文学会20多个动画组件的更多相关文章
- 【Flutter 实战】17篇动画系列文章带你走进自定义动画
老孟导读:Flutter 动画系列文章分为三部分:基础原理和核心概念.系统动画组件.8篇自定义动画案例,共17篇. 动画核心概念 在开发App的过程中,自定义动画必不可少,Flutter 中想要自定义 ...
- 一文学会JVM性能优化
实战性能优化 1 重新认知JVM 之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程,现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图. 执行引擎:用于执行JVM字 ...
- [实战]MVC5+EF6+MySql企业网盘实战(23)——文档列表
写在前面 上篇文章实现了图片列表,这篇文章实现文档列表将轻车熟路,因为逻辑基本相似,只是查询条件的不同.这里将txt,doc,docx,ppt,pptx,xls,xlsx的文件都归为文档列表中. 系列 ...
- JavaEE实战——XML文档DOM、SAX、STAX解析方式详解
原 JavaEE实战--XML文档DOM.SAX.STAX解析方式详解 2016年06月22日 23:10:35 李春春_ 阅读数:3445 标签: DOMSAXSTAXJAXPXML Pull 更多 ...
- 【Flutter实战】移动技术发展史
老孟导读:大家好,这是[Flutter实战]系列文章的第一篇,这并不是一篇Flutter技术文章,而是介绍智能手机操作系统.跨平台技术的演进以及我对各种跨平台技术看法的文章. 智能手机操作系统 塞班( ...
- Flutter实战】文本组件及五大案例
老孟导读:大家好,这是[Flutter实战]系列文章的第二篇,这一篇讲解文本组件,文本组件包括文本展示组件(Text和RichText)和文本输入组件(TextField),基础用法和五个案例助你快速 ...
- 【Flutter实战】图片组件及四大案例
老孟导读:大家好,这是[Flutter实战]系列文章的第三篇,这一篇讲解图片组件,Image有很多高级用法,希望对您有所帮助. 图片组件是Flutter基础组件之一,和文本组件一样必不可少.图片组件包 ...
- 【Flutter实战】定位装饰权重组件及柱状图案例
老孟导读:Flutter中有这么一类组件,用于定位.装饰.控制子组件,比如 Container (定位.装饰).Expanded (扩展).SizedBox (固定尺寸).AspectRatio (宽 ...
- 【Flutter实战】自定义滚动条
老孟导读:[Flutter实战]系列文章地址:http://laomengit.com/guide/introduction/mobile_system.html 默认情况下,Flutter 的滚动组 ...
随机推荐
- DJANGO-天天生鲜项目从0到1-002-用户模块-注册
本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...
- 初步知道scss 简化css复杂层级
简介:今天在调试前端样式的时候,el-button组件位置需要调整并且 需要改字体大小 .直接上了一个 font-text:20px; 发现没作用,谷歌调试发现并未作用到组件里的<span> ...
- ElasticSearch(一)概念介绍及环境搭建
一.什么是ElasticSearch: Elasticsearch (ES)是一个基于Lucene构建的开源.分布式.RESTful 接口全文搜索引擎.Elasticsearch 还是一个分布式文档数 ...
- 初识Http
HTTP是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以及相关的各种控制 和错误处理方式. HTTP专门用来在两点之间传输数据,不能用于广播.寻址或路由. HTTP传输的是文 ...
- 5.pandas新增数据列
有的时候,表格自带的数据根本没有办法满足我们,我们经常会新加一列数据或者对原有的数据进行修改 还是接着上篇文章的数据进行操作 直接赋值 我想算一下每一天的温差 df.loc[:, 'wencha'] ...
- 【扩展推荐】Intervention/image 图片处理
Intervention/image 是为 Laravel 定制的图片处理工具, 它提供了一套易于表达的方式来创建.编辑图片. 一.环境要求 二.安装及配置 下载地址:https://packagis ...
- zookeeper 源码编译
环境:mac 1.github上下载 源码 项目地址:https://github.com/apache/zookeeper 2.安装 ant mac:brew update -> brew ...
- python基础day1&2
解决中文乱码问题 在开头加上 -*- encoding:utf-8 -*- if条件 if 条件: 结果#if elif else是单选,只走一条路 num = input('Please input ...
- PHP array_merge_recursive() 函数
实例 把两个数组合并为一个数组: <?php$a1=array("a"=>"red","b"=>"green&q ...
- PHP move_uploaded_file() 函数
定义和用法 move_uploaded_file() 函数把上传的文件移动到新位置. 如果成功该函数返回 TRUE,如果失败则返回 FALSE. 语法 move_uploaded_file(file, ...

