跳到主内容

动画 API 概览

动画概念概览。

Flutter 中的动画系统基于类型化的 Animation 对象。Widget 既可以在其构建函数中通过读取当前值并监听状态变化来直接整合这些动画,也可以将这些动画作为更复杂动画的基础,传递给其他 Widget。

动画

#

动画系统的主要构建块是 Animation 类。动画表示一个特定类型的值,该值可以在动画的生命周期内发生变化。大多数执行动画的 Widget 都会接收一个 Animation 对象作为参数,从中读取动画的当前值,并监听该值的变化。

addListener

#

每当动画的值发生变化时,动画会通知所有通过 addListener 添加的监听器。通常,监听动画的 State 对象会在其监听器回调中调用自身的 setState,以通知 Widget 系统需要使用新的动画值进行重建。

这种模式非常普遍,因此有两个 Widget 可以帮助在动画值改变时重建 Widget:AnimatedWidgetAnimatedBuilder。前者 AnimatedWidget 最适用于无状态的动画 Widget。要使用 AnimatedWidget,只需将其子类化并实现 build 函数即可。后者 AnimatedBuilder 则适用于希望将动画作为大型构建函数一部分的更复杂的 Widget。要使用 AnimatedBuilder,只需构造该 Widget 并传递一个 builder 函数即可。

addStatusListener

#

动画还提供了一个 AnimationStatus,用于指示动画随时间演变的状态。每当动画的状态发生变化时,动画会通知所有通过 addStatusListener 添加的监听器。通常,动画从 dismissed 状态开始,这意味着它们处于范围的起点。例如,从 0.0 到 1.0 的动画,当其值为 0.0 时即为 dismissed。随后,动画可能会以 forward(从 0.0 到 1.0)或 reverse(从 1.0 到 0.0)方式运行。最终,如果动画到达其范围的终点 (1.0),动画将达到 completed 状态。

Animation­Controller

#

要创建动画,首先需要创建一个 AnimationControllerAnimationController 不仅本身是一个动画,还可以让您控制动画。例如,您可以告诉控制器 forward(向前)播放动画或 stop(停止)动画。您还可以 fling(抛掷)动画,它使用物理模拟(如弹簧)来驱动动画。

创建动画控制器后,您可以基于它构建其他动画。例如,您可以创建一个 ReverseAnimation,它镜像原始动画但以相反的方向运行(从 1.0 到 0.0)。同样,您可以创建一个 CurvedAnimation,其值由 Curve 进行调整。

Tweens

#

要实现 0.0 到 1.0 区间之外的动画,可以使用 Tween<T>,它在 beginend 值之间进行插值。许多类型都有特定的 Tween 子类,提供类型特定的插值。例如,ColorTween 在颜色之间进行插值,RectTween 在矩形之间进行插值。您可以通过创建自己的 Tween 子类并覆盖其 lerp 函数来定义自己的插值。

Tween 本身只定义了如何在两个值之间进行插值。为了获得动画当前帧的具体值,还需要一个动画来确定当前状态。有两种方法可以将 Tween 与动画结合以获得具体值:

  1. 您可以在动画的当前值处 evaluate Tween。这种方法最适用于已经监听动画并因此在动画值变化时进行重建的 Widget。

  2. 您可以基于动画来 animate Tween。animate 函数不会返回单个值,而是返回一个包含该 Tween 的新 Animation。当您希望将新创建的动画提供给另一个 Widget 时,这种方法最为有用,该 Widget 可以读取包含该 Tween 的当前值,并监听值的变化。

架构

#

动画实际上是由多个核心构建块组成的。

Scheduler

#

SchedulerBinding 是一个单例类,它暴露了 Flutter 的调度原语。

在此讨论中,关键的原语是帧回调。每当需要在屏幕上显示一帧时,Flutter 引擎就会触发一个“begin frame”回调,调度程序将其多路复用到所有使用 scheduleFrameCallback() 注册的监听器上。所有这些回调都会获得该帧的正式时间戳,以相对于某个任意纪元的 Duration 形式表示。由于所有回调具有相同的时间,因此从这些回调触发的任何动画即使在执行时耗费了几毫秒,看起来也将完全同步。

Tickers

#

Ticker 类挂接到调度程序的 scheduleFrameCallback() 机制中,以便在每次滴答时调用回调。

Ticker 可以启动和停止。启动时,它会返回一个 Future,当它停止时该 Future 将解析。

在每次滴答时,Ticker 会向回调提供自启动后第一次滴答以来的持续时间。

因为 Ticker 总是提供相对于启动后第一次滴答的流逝时间,所以所有的 Ticker 都是同步的。如果您在两次滴答之间的不同时间启动三个 Ticker,它们仍然会与相同的开始时间同步,并随后同步滴答。就像公交车站的人一样,所有的 Ticker 都会等待一个定期发生的事件(滴答)来开始移动(计算时间)。

Simulations

#

Simulation 抽象类将相对时间值(流逝时间)映射到一个 double 值,并具有完成的概念。

原则上,模拟是无状态的,但在实践中,某些模拟(例如 BouncingScrollSimulationClampingScrollSimulation)在查询时会发生不可逆的状态改变。

对于不同的效果,Simulation 类有 各种具体实现

Animatables

#

Animatable 抽象类将 double 映射为特定类型的值。

Animatable 类是无状态且不可变的。

Tweens

#

Tween<T> 抽象类将名义上在 0.0-1.0 范围内的 double 值映射为类型化的值(例如 Color 或另一个 double)。它是一个 Animatable

它具有输出类型 (T) 的概念,以及该类型的 begin 值和 end 值,还有一种为给定输入值(名义上在 0.0-1.0 范围内的 double)在开始值和结束值之间进行插值 (lerp) 的方法。

Tween 类是无状态且不可变的。

组合 Animatable

#

Animatable<double>(父级)传递给 Animatablechain() 方法,会创建一个新的 Animatable 子类,该子类先应用父级的映射,然后再应用子级的映射。

Curves

#

Curve 抽象类将名义上在 0.0-1.0 范围内的 double 值映射为名义上在 0.0-1.0 范围内的 double 值。

Curve 类是无状态且不可变的。

动画

#

Animation 抽象类提供给定类型的值、动画方向和动画状态的概念,以及用于注册在值或状态改变时被调用的回调的监听器接口。

某些 Animation 子类的值永远不会改变(kAlwaysCompleteAnimationkAlwaysDismissedAnimationAlwaysStoppedAnimation);在这些对象上注册回调没有效果,因为回调永远不会被调用。

Animation<double> 变体比较特殊,因为它可以用来表示名义上在 0.0-1.0 范围内的 double,这是 CurveTween 类以及某些 Animation 子类所期望的输入。

某些 Animation 子类是无状态的,仅将监听器转发给其父级。有些则是有状态的。

可组合动画

#

大多数 Animation 子类都采用明确的“父级” Animation<double>。它们由该父级驱动。

CurvedAnimation 子类采用 Animation<double> 类(父级)和几个 Curve 类(向前和向后曲线)作为输入,并使用父级的值作为曲线的输入来确定其输出。CurvedAnimation 是不可变且无状态的。

ReverseAnimation 子类采用 Animation<double> 类作为其父级,并反转动画的所有值。它假设父级使用的值名义上在 0.0-1.0 范围内,并返回 1.0-0.0 范围内的值。父动画的状态和方向也会被反转。ReverseAnimation 是不可变且无状态的。

ProxyAnimation 子类采用 Animation<double> 类作为其父级,并仅转发该父级的当前状态。但是,父级是可变的。

TrainHoppingAnimation 子类有两个父级,并在它们的值发生交叉时在两者之间切换。

动画控制器

#

AnimationController 是一个有状态的 Animation<double>,它使用 Ticker 为自身提供生命力。它可以启动和停止。在每次滴答时,它会将自启动以来的流逝时间传递给 Simulation 以获得一个值。这就是它报告的值。如果 Simulation 报告此时已结束,则控制器会自动停止。

可以为动画控制器设置一个下限和上限以便在其中进行动画处理,以及一个持续时间。

在简单情况下(使用 forward()reverse()),动画控制器仅在给定的持续时间内从下限到上限(或反之,对于反向)进行线性插值。

使用 repeat() 时,动画控制器在给定的持续时间内在给定的边界之间进行线性插值,但不会停止。

使用 animateTo() 时,动画控制器在给定的持续时间内从当前值线性插值到给定的目标值。如果未向方法提供持续时间,则使用控制器的默认持续时间以及由控制器下限和上限描述的范围来确定动画的速度。

使用 fling() 时,会使用 Force 创建特定的模拟,然后将其用于驱动控制器。

使用 animateWith() 时,给定的模拟将用于驱动控制器。

这些方法都会返回 Ticker 提供的 future,当控制器下次停止或更改模拟时,该 future 将解析。

将 Animatable 附加到动画

#

Animation<double>(新父级)传递给 Animatableanimate() 方法,会创建一个新的 Animation 子类,其行为类似于 Animatable,但由给定的父级驱动。