精心设计的动画让 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 创建自定义显式动画”

动画类型

#

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

补间动画

#

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

基于物理的动画

#

在基于物理的动画中,运动被模拟成类似于真实世界的行为。例如,当你抛出一个球时,它在哪里以及何时落地取决于抛球的速度以及它离地面的距离。同样,一个系在弹簧上的球落地(并弹跳)的方式与一个系在绳子上的球落地的方式不同。

常见动画模式

#

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

动画列表或网格

#

这种模式涉及对列表中或网格中元素的添加或删除进行动画处理。

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

共享元素过渡

#

在此模式中,用户从页面中选择一个元素(通常是图像),然后 UI 将选定的元素动画到包含更多详细信息的新页面。在 Flutter 中,您可以使用 `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>。一个 Animatable,像 Animation 一样,不一定输出 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 对象可以有 Listeners 和 StatusListeners,通过 addListener()addStatusListener() 定义。当动画的值改变时,就会调用 ListenerListener 最常见的行为是调用 setState() 来触发重建。当动画开始、结束、向前或向后移动时,就会调用 StatusListener,这由 AnimationStatus 定义。

代码实验室、教程和文章

#

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

  • 隐式动画代码实验室
    通过分步说明和交互式示例,涵盖如何使用隐式动画。

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

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

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

其他资源

#

请通过以下链接了解更多 Flutter 动画知识