老孟导读:此篇文章是 Flutter 动画系列文章第三篇,后续还有动画序列、过度动画、转场动画、自定义动画等。

Flutter 系统提供了20多个动画组件,只要你把前面【动画核心】(文末有链接)的文章看明白了,这些组件对你来说是非常轻松的,这些组件大部分都是对常用操作的封装。

显示动画组件

回顾上一篇【动画核心】的文章中创建动画三个必须的步骤:

  1. 创建 AnimationController。
  2. 监听 AnimationController,调用 setState 刷新UI。
  3. 释放 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();
}
}

自定义的动画组件只有两个功能:

  1. 监听 AnimationController,调用 setState
  2. 释放 AnimationController。

而 AnimationController 的创建需要开发者自行创建,为什么封装在自定义组件内?这个后面会介绍。

其实这个组件不用我们自己封装,因为系统已经封装好了,在学习 Flutter 的过程中自定义组件是非常重要的,因此多封装一些组件,即使是系统已经存在的,用自己和系统的进行对比,可以极大的提高我们自定义组件的能力。

系统封装的类似上面的组件是 AnimatedWidget,此类是抽象类,源代码:

区别:

  1. 我们使用 监听 AnimationController,调用 setState ,而系统使用 Listenable,Listenable 是一个维护侦听器列表的对象,用于通知客户端该对象已被更新。

    Listenable 有两个变体:

    1. ValueListenable :扩展[Listenable]接口的接口,具有当前值的概念。
    2. Animation:一个扩展[ValueListenable]接口的接口,添加方向(正向或反向)的概念。

    AnimationController 的继承结构:

    AnimationController 也是继承自 Listenable,因此使用 Listenable 适用的范围更广,不仅仅可以用于 Animation ,还可以用于 ChangeNotifier。

  2. 由于使用了 Listenable,因此监听和释放使用listenable.addListenerlistenable.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,我们称之为隐式动画组件,隐式动画组件有: AnimatedAlignAnimatedContainerAnimatedDefaultTextStyleAnimatedOpacityAnimatedPaddingAnimatedPhysicalModelAnimatedPositionedAnimatedPositionedDirectionalAnimatedThemeSliverAnimatedOpacityTweenAnimationBuilderAnimatedContainer 等。
  • 显示动画组件:需要设置 AnimationController,控制动画的执行,使用显式动画可以完成任何隐式动画的效果,甚至功能更丰富一些,不过你需要管理该动画的 AnimationController 生命周期,AnimationController 并不是一个控件,所以需要将其放在 stateful 控件中。显示动画组件有:AlignTransitionAnimatedBuilderAnimatedModalBarrierDecoratedBoxTransitionDefaultTextStyleTransitionPositionedTransitionRelativePositionedTransitionRotationTransitionScaleTransitionSizeTransitionSlideTransitionFadeTransition 等。

不难看出,使用隐式动画控件,代码更简单,而且无需管理 AnimationController 的生命周期,有人觉得隐式动画组件多方便啊,为什么还要显示动画组件呢?因为:封装的越复杂,使用越简单,往往伴随着功能越不丰富。比如想让动画一直重复执行,隐式动画组件是无法实现的。

显示动画组件和隐式动画组件中各有一个万能的组件,它们是 AnimatedBuilderTweenAnimationBuilder,当系统中不存在我们想要的动画组件时,可以使用这两个组件,以 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,
);
},
),
);
}
}

AnimatedBuilderTweenAnimationBuilder 本质上和其他动画组件没有区别,只是给了我们更高的灵活性。

如何选取

Flutter 内置的动画组件分为两种:隐式动画组件显示动画组件 ,显示动画组件只封装了 setState 方法,需要开发者创建 AnimationController,并管理 AnimationController。隐式动画组件封装了 AnimationController、Curve、Tween,只需提供给组件动画开始、结束值,其余由系统管理。

隐式动画组件可以完成效果,显示动画组件都可以完成,那么什么时候使用隐式动画组件?什么时候使用显示动画组件?

  1. 判断你的动画组件是否一直重复,比如一直转圈的loading动画,如果是选择显式动画。
  2. 判断你的动画组件是否需要多个组件联动,如果是选择显式动画。
  3. 判断你的动画组件是否需要组合动画,如果是选择显式动画。
  4. 如果上面三个条件都是否,就选择隐式动画组件,判断是否已经内置动画组件,如果没有,使用 TweenAnimationBuilder,有就直接使用内置动画组件。
  5. 选择显式动画组件,判断是否已经内置动画组件,如果没有,使用 AnimatedBuilder,有就直接使用内置动画组件。

逻辑图如下:

还有一个简单的区分办法:如果你的动画相对比较简单,动画从一种状态过渡到另一种状态,不需要单独控制 AnimationController,这种情况下,隐式动画组件一般可以就可以实现。

不过也没有必要特别纠结使用隐式动画组件还是显示动画组件,不管使用哪一种,实现效果即可。

交流

老孟Flutter博客地址(330个控件用法):http://laomengit.com

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

【Flutter 实战】一文学会20多个动画组件的更多相关文章

  1. 【Flutter 实战】17篇动画系列文章带你走进自定义动画

    老孟导读:Flutter 动画系列文章分为三部分:基础原理和核心概念.系统动画组件.8篇自定义动画案例,共17篇. 动画核心概念 在开发App的过程中,自定义动画必不可少,Flutter 中想要自定义 ...

  2. 一文学会JVM性能优化

    实战性能优化 1 重新认知JVM 之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程,现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图. 执行引擎:用于执行JVM字 ...

  3. [实战]MVC5+EF6+MySql企业网盘实战(23)——文档列表

    写在前面 上篇文章实现了图片列表,这篇文章实现文档列表将轻车熟路,因为逻辑基本相似,只是查询条件的不同.这里将txt,doc,docx,ppt,pptx,xls,xlsx的文件都归为文档列表中. 系列 ...

  4. JavaEE实战——XML文档DOM、SAX、STAX解析方式详解

    原 JavaEE实战--XML文档DOM.SAX.STAX解析方式详解 2016年06月22日 23:10:35 李春春_ 阅读数:3445 标签: DOMSAXSTAXJAXPXML Pull 更多 ...

  5. 【Flutter实战】移动技术发展史

    老孟导读:大家好,这是[Flutter实战]系列文章的第一篇,这并不是一篇Flutter技术文章,而是介绍智能手机操作系统.跨平台技术的演进以及我对各种跨平台技术看法的文章. 智能手机操作系统 塞班( ...

  6. Flutter实战】文本组件及五大案例

    老孟导读:大家好,这是[Flutter实战]系列文章的第二篇,这一篇讲解文本组件,文本组件包括文本展示组件(Text和RichText)和文本输入组件(TextField),基础用法和五个案例助你快速 ...

  7. 【Flutter实战】图片组件及四大案例

    老孟导读:大家好,这是[Flutter实战]系列文章的第三篇,这一篇讲解图片组件,Image有很多高级用法,希望对您有所帮助. 图片组件是Flutter基础组件之一,和文本组件一样必不可少.图片组件包 ...

  8. 【Flutter实战】定位装饰权重组件及柱状图案例

    老孟导读:Flutter中有这么一类组件,用于定位.装饰.控制子组件,比如 Container (定位.装饰).Expanded (扩展).SizedBox (固定尺寸).AspectRatio (宽 ...

  9. 【Flutter实战】自定义滚动条

    老孟导读:[Flutter实战]系列文章地址:http://laomengit.com/guide/introduction/mobile_system.html 默认情况下,Flutter 的滚动组 ...

随机推荐

  1. DJANGO-天天生鲜项目从0到1-002-用户模块-注册

    本项目基于B站UP主‘神奇的老黄’的教学视频‘天天生鲜Django项目’,视频讲的非常好,推荐新手观看学习 https://www.bilibili.com/video/BV1vt41147K8?p= ...

  2. 初步知道scss 简化css复杂层级

    简介:今天在调试前端样式的时候,el-button组件位置需要调整并且 需要改字体大小 .直接上了一个 font-text:20px; 发现没作用,谷歌调试发现并未作用到组件里的<span> ...

  3. ElasticSearch(一)概念介绍及环境搭建

    一.什么是ElasticSearch: Elasticsearch (ES)是一个基于Lucene构建的开源.分布式.RESTful 接口全文搜索引擎.Elasticsearch 还是一个分布式文档数 ...

  4. 初识Http

     HTTP是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以及相关的各种控制 和错误处理方式.  HTTP专门用来在两点之间传输数据,不能用于广播.寻址或路由. HTTP传输的是文 ...

  5. 5.pandas新增数据列

    有的时候,表格自带的数据根本没有办法满足我们,我们经常会新加一列数据或者对原有的数据进行修改 还是接着上篇文章的数据进行操作 直接赋值 我想算一下每一天的温差 df.loc[:, 'wencha'] ...

  6. 【扩展推荐】Intervention/image 图片处理

    Intervention/image 是为 Laravel 定制的图片处理工具, 它提供了一套易于表达的方式来创建.编辑图片. 一.环境要求 二.安装及配置 下载地址:https://packagis ...

  7. zookeeper 源码编译

    环境:mac 1.github上下载 源码 项目地址:https://github.com/apache/zookeeper 2.安装 ant mac:brew update ->  brew ...

  8. python基础day1&2

    解决中文乱码问题 在开头加上 -*- encoding:utf-8 -*- if条件 if 条件: 结果#if elif else是单选,只走一条路 num = input('Please input ...

  9. PHP array_merge_recursive() 函数

    实例 把两个数组合并为一个数组: <?php$a1=array("a"=>"red","b"=>"green&q ...

  10. PHP move_uploaded_file() 函数

    定义和用法 move_uploaded_file() 函数把上传的文件移动到新位置. 如果成功该函数返回 TRUE,如果失败则返回 FALSE. 语法 move_uploaded_file(file, ...