精心设计的动画让 UI 更直观,有助于提升应用的光滑外观和流畅体验,并改善用户体验。Flutter 的动画支持使得实现各种动画类型变得容易。许多组件,特别是 Material 组件,都带有其设计规范中定义的标准运动效果,但也可以自定义这些效果。

选择方法

#

在 Flutter 中创建动画有多种方法。哪种方法适合你?为了帮助你做出决定,请观看视频《如何选择适合你的 Flutter 动画组件?》(也作为配套文章发布)。

在新标签页中观看 YouTube 视频:“如何选择适合你用例的 Flutter 动画组件”

(要深入了解决策过程,请观看在 Flutter Europe 上展示的视频《正确地实现 Flutter 动画》。)

如视频所示,以下决策树有助于你决定在实现 Flutter 动画时采用哪种方法

The animation decision tree

动画深入解析

#

要更深入地了解 Flutter 中动画的工作原理,请观看《动画深入解析》。(也作为配套文章发布。)

在新标签页中观看 YouTube 视频:“深入了解 Flutter 动画”

隐式动画和显式动画

#

预打包的隐式动画

#

如果预打包的隐式动画(最容易实现的动画)符合你的需求,请观看《隐式动画基础》。(也作为配套文章发布。)

在新标签页中观看 YouTube 视频:“Flutter 隐式动画基础”

自定义隐式动画

#

要创建自定义隐式动画,请观看《使用 TweenAnimationBuilder 创建自己的自定义隐式动画》。(也作为配套文章发布。)

在新标签页中观看 YouTube 视频:“使用 TweenAnimationBuilder 创建自定义隐式动画”

内置隐式动画

#

要创建显式动画(其中你控制动画,而不是让框架控制它),你或许可以使用内置的显式动画类之一。欲了解更多信息,请观看《使用内置显式动画创建你的第一个定向动画》。(也作为配套文章发布。)

在新标签页中观看 YouTube 视频:“使用内置显式动画创建你的第一个定向动画”

显式动画

#

如果你需要从头开始构建显式动画,请观看《使用 AnimatedBuilder 和 AnimatedWidget 创建自定义显式动画》。(也作为配套文章发布。)

在新标签页中观看 YouTube 视频:“使用 AnimatedBuilder 和 AnimatedWidget 创建自定义显式动画”

动画类型

#

通常,动画要么是基于补间 (tween) 的,要么是基于物理的。以下部分解释了这些术语的含义,并为你指明了可以了解更多信息的资源。

补间动画

#

in-betweening 的缩写。在补间动画中,定义了起始点和结束点,以及时间线和定义过渡时间与速度的曲线。框架计算如何从起始点过渡到结束点。

基于物理的动画

#

在基于物理的动画中,运动被建模以模仿真实世界的行为。例如,当你抛出一个球时,它落地的位置和时间取决于它被抛出的速度以及它离地面的距离。同样,一个系在弹簧上的球的下落(和反弹)方式与一个系在绳子上的球的下落方式不同。

常见的动画模式

#

大多数 UX 或动效设计师发现,在设计 UI 时,某些动画模式被重复使用。本节列出了一些常用的动画模式,并告诉你到哪里学习更多。

动画列表或网格

#

此模式涉及为列表或网格中元素的添加或移除添加动画效果。

  • AnimatedList 示例
    此演示来自示例应用目录,展示了如何为向列表中添加元素或移除选定元素添加动画效果。当用户使用加 (+) 和减 (-) 按钮修改列表时,内部 Dart 列表会同步更新。

共享元素过渡

#

在此模式中,用户从页面中选择一个元素——通常是图片——然后 UI 将所选元素动画化到包含更多详细信息的新页面。在 Flutter 中,你可以使用 Hero 组件轻松实现在路由(页面)之间共享元素的过渡效果。

  • Hero 动画 如何创建两种 Hero 动画样式

    • 英雄元素从一个页面飞到另一个页面,同时改变其位置和大小。
    • 英雄元素的边界形状从圆形变为方形,同时从一个页面飞到另一个页面。
  • 另请参阅 HeroNavigatorPageRoute 类的 API 文档。

交错动画

#

动画被分解成更小的动作,其中一些动作被延迟。这些较小的动画可以是顺序的,也可以是部分或完全重叠的。

基本动画概念和类

#

Flutter 中的动画系统基于类型化的 Animation 对象。组件可以直接在其 build 函数中通过读取其当前值并监听其状态变化来整合这些动画,或者它们可以使用这些动画作为更复杂动画的基础,并将其传递给其他组件。

Animation<double>

#

在 Flutter 中,Animation 对象对屏幕上显示的内容一无所知。Animation 是一个抽象类,它理解其当前值及其状态(已完成或已解除)。其中一种更常用的动画类型是 Animation<double>

Animation 对象在特定持续时间内,在两个值之间按顺序生成插值数字。Animation 对象的输出可以是线性的、曲线的、阶梯函数或任何你可以创建的其他映射。根据 Animation 对象的控制方式,它可以反向运行,甚至在中间切换方向。

动画也可以插值除了 double 之外的类型,例如 Animation<Color>Animation<Size>

Animation 对象有状态。它的当前值始终可以通过 .value 成员获取。

Animation 对象对渲染或 build() 函数一无所知。

CurvedAnimation

#

一个 CurvedAnimation 将动画的进度定义为非线性曲线。

dart
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);

CurvedAnimationAnimationController(在接下来的部分中描述)都属于 Animation<double> 类型,因此你可以互换地传递它们。CurvedAnimation 包装了它正在修改的对象——你不需要子类化 AnimationController 来实现曲线。

你可以将 CurvesCurvedAnimation 一起使用。Curves 类定义了许多常用的曲线,或者你可以创建自己的曲线。例如

dart
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 对象,但并未开始运行它

dart
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

dart
tween = Tween<double>(begin: -200, end: 0);

Tween 是一个无状态对象,只接受 beginendTween 的唯一作用是定义从输入范围到输出范围的映射。输入范围通常是 0.0 到 1.0,但这并非强制要求。

Tween 继承自 Animatable<T>,而不是 Animation<T>。像 Animation 一样,Animatable 不必输出 double 类型。例如,ColorTween 指定了两种颜色之间的渐变。

dart
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 的整数值。

dart
AnimationController controller = AnimationController(
  duration: const Duration(milliseconds: 500),
  vsync: this,
);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller);

以下示例展示了一个控制器、一个曲线和一个 Tween

dart
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 对象可以有 ListenerStatusListener,通过 addListener()addStatusListener() 定义。当动画的值发生变化时,就会调用 ListenerListener 最常见的行为是调用 setState() 以触发重建。当动画开始、结束、前进或反转时,就会调用 StatusListener,具体由 AnimationStatus 定义。

Codelab、教程和文章

#

以下资源是学习 Flutter 动画框架的良好起点。每个文档都展示了如何编写动画代码。

  • 隐式动画 Codelab
    涵盖了如何通过分步说明和交互式示例使用隐式动画。

  • 动画教程
    解释了 Flutter 动画包中的基本类(控制器、Animatable、曲线、监听器、构建器),同时通过使用动画 API 的不同方面,引导你完成一系列补间动画。本教程展示了如何创建自己的自定义显式动画。

  • 从零开始学 Flutter,第一部分第二部分
    Medium 文章,展示了如何使用补间动画创建动画图表。

  • 休闲游戏工具包
    一个包含游戏模板的工具包,其中包含如何使用 Flutter 动画的示例。

其他资源

#

通过以下链接了解更多 Flutter 动画信息