动画简介
精心设计的动画让 UI 更直观,有助于提升应用的光滑外观和流畅体验,并改善用户体验。Flutter 的动画支持使得实现各种动画类型变得容易。许多组件,特别是 Material 组件,都带有其设计规范中定义的标准运动效果,但也可以自定义这些效果。
选择方法
#在 Flutter 中创建动画有多种方法。哪种方法适合你?为了帮助你做出决定,请观看视频《如何选择适合你的 Flutter 动画组件?》(也作为配套文章发布)。
(要深入了解决策过程,请观看在 Flutter Europe 上展示的视频《正确地实现 Flutter 动画》。)
如视频所示,以下决策树有助于你决定在实现 Flutter 动画时采用哪种方法

动画深入解析
#要更深入地了解 Flutter 中动画的工作原理,请观看《动画深入解析》。(也作为配套文章发布。)
隐式动画和显式动画
#预打包的隐式动画
#如果预打包的隐式动画(最容易实现的动画)符合你的需求,请观看《隐式动画基础》。(也作为配套文章发布。)
自定义隐式动画
#要创建自定义隐式动画,请观看《使用 TweenAnimationBuilder 创建自己的自定义隐式动画》。(也作为配套文章发布。)
内置隐式动画
#要创建显式动画(其中你控制动画,而不是让框架控制它),你或许可以使用内置的显式动画类之一。欲了解更多信息,请观看《使用内置显式动画创建你的第一个定向动画》。(也作为配套文章发布。)
显式动画
#如果你需要从头开始构建显式动画,请观看《使用 AnimatedBuilder 和 AnimatedWidget 创建自定义显式动画》。(也作为配套文章发布。)
在新标签页中观看 YouTube 视频:“使用 AnimatedBuilder 和 AnimatedWidget 创建自定义显式动画”
动画类型
#通常,动画要么是基于补间 (tween) 的,要么是基于物理的。以下部分解释了这些术语的含义,并为你指明了可以了解更多信息的资源。
补间动画
#in-betweening 的缩写。在补间动画中,定义了起始点和结束点,以及时间线和定义过渡时间与速度的曲线。框架计算如何从起始点过渡到结束点。
请参阅动画教程,其中示例使用了补间动画。
另请参阅
Tween
、CurveTween
和TweenSequence
的 API 文档。
基于物理的动画
#在基于物理的动画中,运动被建模以模仿真实世界的行为。例如,当你抛出一个球时,它落地的位置和时间取决于它被抛出的速度以及它离地面的距离。同样,一个系在弹簧上的球的下落(和反弹)方式与一个系在绳子上的球的下落方式不同。
使用物理模拟来为组件添加动画
Flutter CookBook 动画章节中的一个范例。另请参阅
AnimationController.animateWith
和SpringSimulation
的 API 文档。
常见的动画模式
#大多数 UX 或动效设计师发现,在设计 UI 时,某些动画模式被重复使用。本节列出了一些常用的动画模式,并告诉你到哪里学习更多。
动画列表或网格
#此模式涉及为列表或网格中元素的添加或移除添加动画效果。
AnimatedList
示例
此演示来自示例应用目录,展示了如何为向列表中添加元素或移除选定元素添加动画效果。当用户使用加 (+) 和减 (-) 按钮修改列表时,内部 Dart 列表会同步更新。
共享元素过渡
#在此模式中,用户从页面中选择一个元素——通常是图片——然后 UI 将所选元素动画化到包含更多详细信息的新页面。在 Flutter 中,你可以使用 Hero
组件轻松实现在路由(页面)之间共享元素的过渡效果。
Hero 动画 如何创建两种 Hero 动画样式
- 英雄元素从一个页面飞到另一个页面,同时改变其位置和大小。
- 英雄元素的边界形状从圆形变为方形,同时从一个页面飞到另一个页面。
交错动画
#动画被分解成更小的动作,其中一些动作被延迟。这些较小的动画可以是顺序的,也可以是部分或完全重叠的。
基本动画概念和类
#Flutter 中的动画系统基于类型化的 Animation
对象。组件可以直接在其 build 函数中通过读取其当前值并监听其状态变化来整合这些动画,或者它们可以使用这些动画作为更复杂动画的基础,并将其传递给其他组件。
Animation<double>
#在 Flutter 中,Animation
对象对屏幕上显示的内容一无所知。Animation
是一个抽象类,它理解其当前值及其状态(已完成或已解除)。其中一种更常用的动画类型是 Animation<double>
。
Animation
对象在特定持续时间内,在两个值之间按顺序生成插值数字。Animation
对象的输出可以是线性的、曲线的、阶梯函数或任何你可以创建的其他映射。根据 Animation
对象的控制方式,它可以反向运行,甚至在中间切换方向。
动画也可以插值除了 double 之外的类型,例如 Animation<Color>
或 Animation<Size>
。
Animation
对象有状态。它的当前值始终可以通过 .value
成员获取。
Animation
对象对渲染或 build()
函数一无所知。
CurvedAnimation
#一个 CurvedAnimation
将动画的进度定义为非线性曲线。
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);
CurvedAnimation
和 AnimationController
(在接下来的部分中描述)都属于 Animation<double>
类型,因此你可以互换地传递它们。CurvedAnimation
包装了它正在修改的对象——你不需要子类化 AnimationController
来实现曲线。
你可以将 Curves
与 CurvedAnimation
一起使用。Curves
类定义了许多常用的曲线,或者你可以创建自己的曲线。例如
import 'dart:math';
class ShakeCurve extends Curve {
@override
double transform(double t) => sin(t * pi * 2);
}
如果你想将动画曲线应用于 Tween
,请考虑使用 CurveTween
。
AnimationController
#AnimationController
是一个特殊的 Animation
对象,它在硬件准备好新帧时生成一个新值。默认情况下,AnimationController
在给定持续时间内线性地生成从 0.0 到 1.0 的数字。例如,此代码创建了一个 Animation
对象,但并未开始运行它
controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
AnimationController
派生自 Animation<double>
,因此可以在任何需要 Animation
对象的地方使用。然而,AnimationController
具有控制动画的额外方法。例如,你可以使用 .forward()
方法启动动画。数字的生成与屏幕刷新相关联,因此通常每秒生成 60 个数字。每个数字生成后,每个 Animation
对象都会调用附加的 Listener
对象。要为每个子项创建自定义显示列表,请参阅 RepaintBoundary
。
创建 AnimationController
时,你需要向其传递 vsync
参数。vsync
的存在可以防止屏幕外动画消耗不必要的资源。你可以通过在类定义中添加 SingleTickerProviderStateMixin
来使用你的有状态对象作为 vsync。你可以在 GitHub 上的 animate1 中看到一个示例。
Tween
#默认情况下,AnimationController
对象的范围是 0.0 到 1.0。如果你需要不同的范围或不同的数据类型,可以使用 Tween
来配置动画以插值到不同的范围或数据类型。例如,下面的 Tween
从 -200.0 到 0.0
tween = Tween<double>(begin: -200, end: 0);
Tween
是一个无状态对象,只接受 begin
和 end
。Tween
的唯一作用是定义从输入范围到输出范围的映射。输入范围通常是 0.0 到 1.0,但这并非强制要求。
Tween
继承自 Animatable<T>
,而不是 Animation<T>
。像 Animation
一样,Animatable
不必输出 double 类型。例如,ColorTween
指定了两种颜色之间的渐变。
colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54);
Tween
对象不存储任何状态。相反,它提供了 evaluate(Animation<double> animation)
方法,该方法使用 transform
函数将动画的当前值(在 0.0 和 1.0 之间)映射到实际的动画值。
Animation
对象的当前值可以在 .value
方法中找到。evaluate 函数还会执行一些内务处理,例如确保当动画值为 0.0 和 1.0 时分别返回 begin 和 end。
Tween.animate
#要使用 Tween
对象,请在 Tween
上调用 animate()
,并传入控制器对象。例如,以下代码在 500 毫秒内生成从 0 到 255 的整数值。
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller);
以下示例展示了一个控制器、一个曲线和一个 Tween
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
final Animation<double> curve = CurvedAnimation(
parent: controller,
curve: Curves.easeOut,
);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(curve);
动画通知
#一个 Animation
对象可以有 Listener
和 StatusListener
,通过 addListener()
和 addStatusListener()
定义。当动画的值发生变化时,就会调用 Listener
。Listener
最常见的行为是调用 setState()
以触发重建。当动画开始、结束、前进或反转时,就会调用 StatusListener
,具体由 AnimationStatus
定义。
Codelab、教程和文章
#以下资源是学习 Flutter 动画框架的良好起点。每个文档都展示了如何编写动画代码。
隐式动画 Codelab
涵盖了如何通过分步说明和交互式示例使用隐式动画。动画教程
解释了 Flutter 动画包中的基本类(控制器、Animatable
、曲线、监听器、构建器),同时通过使用动画 API 的不同方面,引导你完成一系列补间动画。本教程展示了如何创建自己的自定义显式动画。从零开始学 Flutter,第一部分 和 第二部分
Medium 文章,展示了如何使用补间动画创建动画图表。休闲游戏工具包
一个包含游戏模板的工具包,其中包含如何使用 Flutter 动画的示例。
其他资源
#通过以下链接了解更多 Flutter 动画信息