翻译: flutter_animate 动画库使用:

https://pub.dev/packages/flutter_animate

dependencies:
  flutter_animate: ^4.2.0

Flutter动画库

一款高性能的库,可以在Flutter中轻松添加几乎任何类型的动画效果。

  1. 预构建的效果,如淡入淡出、缩放、滑动、翻转、模糊、抖动、闪烁、阴影、交叉淡化过渡、沿路径跟随和颜色效果(饱和度、颜色和色调)
  2. 简单的自定义效果和简化的动画构建器
  3. 将动画与滚动、通知器或其他任何东西同步
  4. 集成事件

所有这些功能都通过一个简单、统一的API实现,无需烦恼地使用AnimationController和StatefulWidget。

基本动画视觉效果同步动画

上图:包含的示例应用程序。

Duration扩展

num添加扩展方法,使指定持续时间更容易。例如:2.seconds0.1.minutes300.ms

AnimatedController扩展

AnimatedControllerloop扩展方法与repeat相同,但添加了一个count参数来指定播放次数。

基础知识

语法

要应用效果,将目标小部件包裹在 Animate 中,并指定一个效果列表:

Animate(
  effects: [FadeEffect(), ScaleEffect()],
  child: Text("Hello World!"),
)

它还为所有小部件添加了 .animate() 扩展方法,它将小部件包装在 Animate() 中。每个效果还会向 Animate 添加一个可链式调用的扩展方法,以启用简写语法:

Text("Hello World!").animate().fade().scale()

注意: 在本文档中使用了简写样式,但无论使用哪种格式,所有功能均可用。

延迟、持续时间和曲线

效果具有可选的 delay、duration 和 curve 参数。效果并行运行,但可以使用 delay 使它们按顺序运行:

Copy code
Text("Hello").animate()
  .fade(duration: 500.ms)
  .scale(delay: 500.ms) // 在淡入淡出之后运行

请注意,效果在整个动画的持续时间内处于"活动"状态,因此例如,同一目标上的两个淡入淡出效果可能会产生意外结果(下面详细介绍的 SwapEffect 可以帮助解决这个问题)。

如果未指定(或为 null),这些值将从上一个效果继承,或者如果它是第一个效果,则从 Animate.defaultDuration 和 Animate.defaultCurve 继承:

Copy code
Text("Hello World!").animate()
  .fadeIn() // 使用 `Animate.defaultDuration`
  .scale() // 从 fadeIn 继承持续时间
  .move(delay: 300.ms, duration: 600.ms) // 使用新的持续时间在上述效果之后运行
  .blurXY() // 从 move 继承延迟和持续时间

Animate 有自己的 delay 参数,它定义了动画开始播放之前的延迟。与 Effect 上的延迟不同,如果动画重复播放,它仅应用一次。

Text("Hello").animate(
    delay: 1000.ms, // 这个延迟仅在开始时发生一次
    onPlay: (controller) => controller.repeat(), // 循环播放
  ).fadeIn(delay: 500.ms) // 这个延迟在每个循环的开始发生

其他效果参数

大多数效果都包括 begin 和 end 参数,用于指定起始和结束值。通常情况下,这些参数是"智能"的,如果只指定一个参数,另一个参数将默认为"中性"值(即无视觉效果)。如果两个参数都未指定,则效果应使用视觉上令人愉悦的默认值。

// 不透明度为1是"中性"值
Text("Hello").animate().fade() // begin=0,end=1
Text("Hello").animate().fade(begin: 0.5) // end=1
Text("Hello").animate().fade(end: 0.5) // begin=1

许多效果还具有其他影响其行为的参数。如果未指定,这些参数也应使用愉悦的默认值。

Text('Hello').animate().tint(color: Colors.purple)

Sequencing with ThenEffect

ThenEffect 是一个特殊的便利性"效果",它可以更容易地对效果进行排序。它通过建立一个新的基准时间,该时间等于上一个效果的结束时间和自己的可选 delay。所有后续效果的延迟都相对于这个新的基准时间。

在以下示例中,滑动效果将在淡入淡出效果结束后的200毫秒后运行。

Text("Hello").animate()
  .fadeIn(duration: 600.ms)
  .then(delay: 200.ms) // 基准时间=800ms
  .slide()

动画列表

AnimateList 类提供了类似的功能,用于小部件列表,可以通过指定的 interval 对每个子项的动画进行偏移:

Column(children: AnimateList(
  interval: 400.ms,
  effects: [FadeEffect(duration: 300.ms)],
  children: [Text("Hello"), Text("World"),  Text("Goodbye")],
))

// 或使用简写方式:
Column(
  children: [Text("Hello"), Text("World"),  Text("Goodbye")]
    .animate(interval: 400.ms).fade(duration: 300.ms),
)

共享效果

由于 Effect 实例是不可变的,因此可以重复使用它们。这使得在应用程序中创建一个全局的效果集合并在一个地方进行更新变得容易。这对于设计系统也很有用。

MyGlobalEffects.transitionIn = <Effect>[
  FadeEffect(duration: 100.ms, curve: Curves.easeOut),
  ScaleEffect(begin: 0.8, curve: Curves.easeIn)
]

// 然后:
Text('Hello').animate(effects: MyGlobalEffects.transitionIn)

自定义效果和构建器

通过扩展 Effect,编写新的可重用效果非常简单,但是您还可以通过使用 CustomEffect、ToggleEffect 和 SwapEffect 来轻松创建一次性自定义效果。

CustomEffect

CustomEffect 允许您构建自定义的动画效果。只需指定一个接受 context、value 和 child 的 builder 函数。child 是动画的目标小部件(可能已经包装在其他效果中)。

例如,以下代码将在文本后面添加一个背景,并使其从红色渐变为蓝色:

Text("Hello World").animate().custom(
  duration: 300.ms,
  builder: (context, value, child) => Container(
    color: Color.lerp(Colors.red, Colors.blue, value),
    padding: EdgeInsets.all(8),
    child: child, // child 是被动画化的 Text 小部件
  )
)

默认情况下,它提供一个从0-1 的 value(尽管某些曲线可能生成超出此范围的值),该值基于当前时间、持续时间和曲线。您还可以在下面的示例中指定 begin 和 end 值。

Animate 可以创建没有子项的小部件,因此您可以将 CustomEffect 用作简化的构建器。例如,以下代码将构建一个从10开始倒数的文本,并淡出:

Animate().custom(
  duration: 10.seconds,
  begin: 10,
  end: 0,
  builder: (_, value, __) => Text(value.round()),
).fadeOut()

ToggleEffect

ToggleEffect 也提供了构建器功能,但是与 double 不同,它提供了一个布尔值,即在效果结束之前为 true,在效果结束后(即持续时间之后)为 false。

Animate().toggle(
  duration: 2.seconds,
  builder: (_, value, __) => Text(value ? "Before" : "After"),
)

这也可用于激活"Animated"小部件,例如 AnimatedContainer,通过使用最小延迟来切换它们的值:

Animate().toggle(
  duration: 1.ms,
  builder: (_, value, __) => AnimatedContainer(
    duration: 1.seconds,
    color: value ? Colors.red : Colors.green,
  ),
)

SwapEffect

SwapEffect 允许您在指定的时间点交换整个目标小部件:

Text("Before").animate()
  .swap(duration: 900.ms, builder: (_, __) => Text("After"))

这对于创建顺序效果也很有用,通过将目标小部件重新插入,从而有效地清除所有先前的效果:

text.animate().fadeOut(300.ms) // 淡出然后...
  // 重新插入原始小部件并通过新的 Animate 渐变淡入:
  .swap(builder: (_, child) => child.animate().fadeIn())

事件和回调函数

Animate 包括以下回调函数:

  • onInit:内部的 AnimationController 已经初始化
  • onPlay:动画在任何 Animate.delay 之后开始播放
  • onComplete:动画已完成

这些回调函数返回 AnimationController,可以用于操作动画(例如重复、反转等)。

Text("Horrible Pulsing Text")
  .animate(onPlay: (controller) => controller.repeat(reverse: true))
  .fadeOut(curve: Curves.easeInOut)

要获得更细致的回调,可以使用 CallbackEffect 或 ListenEffect。

CallbackEffect

CallbackEffect 允许您在动画中的任意位置添加回调函数。例如,在淡入效果进行到一半时添加回调:

Text("Hello").animate().fadeIn(duration: 600.ms)
  .callback(duration: 300.ms, callback: (_) => print('halfway'))

与其他效果一样,它会继承前面效果的延迟和持续时间:

Text("Hello").animate().scale(delay: 200.ms, duration: 400.ms)
  .callback(callback: (_) => print('scale is done'))

ListenEffect

ListenEffect 允许您注册一个回调函数,在给定的延迟、持续时间、曲线、开始和结束值时接收动画值(作为 double)。

Text("Hello").animate().fadeIn(curve: Curves.easeOutExpo)
  .listen(callback: (value) => print('current opacity: $value'))

上面的示例有效,因为监听效果会从淡入效果继承持续时间和曲线,并且默认使用 begin=0, end=1。

Adapters 和 Controllers

默认情况下,所有动画都由内部的 AnimationController 驱动,并根据经过的时间更新。如果需要更多控制,可以指定自己的外部 controller,或者使用 adapter。还可以设置 autoPlay=false,如果希望手动启动动画。

适配器将 AnimationController 与外部源同步。例如,ScrollAdapter 根据 ScrollController 更新动画,以便根据滚动交互运行复杂的动画。

仍然可以使用持续时间来定义动画,但是外部源必须提供 0-1 的值。

Flutter Animate 提供了一系列有用的适配器。请查看适配器了解更多信息。

对状态变化的反应

Animate 可以像 "Animated" 小部件(例如 AnimatedOpacity)一样对状态变化作出反应。只需正常设置动画,但设置一个 target 值。当 target 的值发生变化时,它将自动动画到新的目标位置(其中 0 是开始,1 是结束)。

例如,与通过 setState 切换 _over 的逻辑组合在一起,这将在鼠标悬停时淡出并缩放按钮:

MyButton().animate(target: _over ? 1 : 0)
  .fade(end: 0.8).scaleXY(end: 1.1)

测试动画

在测试动画时,可以将 Animate.restartOnHotReload=true 设置为 true,这样每次热重载应用程序时,所有动画都将自动重新启动。

安装

pub.dev 获取它。