乐于分享
好东西不私藏

Flutter:这个插件包会让你从此爱上动画

Flutter:这个插件包会让你从此爱上动画

在 Flutter 中做动画并不容易。方法五花八门,插件也多如牛毛。我试着对这个课题进行了系统化梳理,并写了几篇文章。

从简单易用到灵活复杂,Flutter 动画可以划分为以下几个层级:

  • • AnimatedX 系列组件,以及 animate_do 和 flutter_animate 插件[1]
  • • TweenAnimationBuilder(补间动画构建器)[2]
  • • 使用 AnimationController 的显式动画[3]

现在出现了一个名为 Cue[4] 的新包,它将取代上述所有方案。

以下是该插件页面上一些精美的示例:

image.png
image.png
image.png

示例看起来不错,但你可能还是会问:这个插件到底有什么特别之处?

首先,它是“物理优先(Physics-first)”的;它默认使用弹簧(Springs)而非曲线(Curves),因此动画看起来更加自然。

但最重要的一点是,它改变了我们在页面上构建组件动画的思维方式。

Cue 并不是让你一个接一个地为组件添加动画,而是强制我们将整个屏幕(或其一部分)视为一个完整的“动画舞台”。

为了理解上述理念,我们需要一个实例。

我们可以看到,当页面加载时,有三个文本组件(Text Widgets)触发了动画。

代码如下:

Cue.onMount(             motion: CueMotion.smooth(),             child: Column(               mainAxisAlignment: MainAxisAlignment.center,               mainAxisSize: MainAxisSize.min,               spacing: 100,               children: [                 Actor(                   acts: [                     Act.fadeIn(from: 0.0),                     ScaleAct.keyframed(                       frames: Keyframes([                         .key(0.92),                         .key(1.6, motion: .bouncy()),                         .key(1.0),                       ], motion: .smooth()),                     ),                   ],                   delay: Duration(milliseconds: 300),                   child: EzText('Child of Actor #1', fontSize: 22),                 ),                 Actor(                   acts: [Act.fadeIn(from: 0.0), Act.slideX(from: -5, to: 0.0)],                   delay: Duration(milliseconds: 600),                   child: EzText('Child of Actor #2', fontSize: 22),                 ),                 Actor(                   acts: [Act.fadeIn(from: 0.0), Act.slideY(from: 5, to: 0.0)],                   delay: Duration(milliseconds: 900),                   child: EzText('Child of Actor #3', fontSize: 22),                 ),               ],             ),           ), 

动画是由两个组件协同完成的:Cue 和 Actor

Cue 负责定义触发动画的事件以及运动的类型:

Cue.onMount(  motion: CueMotion.smooth(),  ...

有很多事件可供选择:onMount(挂载时)、onToggle(切换时)、onChange(改变时)等等(详情请查阅文档[5])。

CueMotion 类允许你从多种预定义的运动类型中进行选择:smooth(平滑)、snappy(干脆)、interactive(交互式)等。

Actor 负责包装需要执行动画的组件。Actor 必须始终位于 Cue(动画场景)内部。此外,Actor 还可以嵌套使用。

Actor(  acts: [Act.fadeIn(from: 0.0), Act.slideY(from: 5, to: 0.0)],  delay: Duration(milliseconds: 900),  child: EzText('Child of Actor #3', fontSize: 22),  ),

EzText[6] 只是我自己封装的文本组件。)

Act 类提供了一系列效果集,例如:scale(缩放)、fadeIn(淡入)、slideX(水平位移)等。

让我们再次回顾一下这个示例 GIF:

image.png

该示例的源代码具有以下结构:

Cue.onToggle( //定义触发方式与运动类型  ...      Actor ( //外层 Actor:负责整个卡片的动画效果。 ...          Actor ( //内层 Actor:负责标题的动画效果。  ...              Actor ( // 另一个内层 Actor:负责正文内容的动画效果。...

这段动画并不是通过给不同的组件分别施加独立的效果而创建的;它在某种程度上是作为一个整体被设计出来的。我认为这带来了巨大的差异。

那我们可以用 Cue 来为单个组件制作动画吗?当然可以。在这种情况下,我们可以省略 Actor,让代码变得更加简洁:

Cue.onMount(  motion: .smooth(),  acts: [    .fadeIn(),    .slideY(from: 0.2),  ],  child: const Text('Hello Cue'),)

注意这种简写语法。

关键帧(Keyframes)

这是我第一个示例中的代码:

Actor( acts: [   ScaleAct.keyframed(     frames: Keyframes([       Keyframe.key(0.92),       .key(1.6, motion: .bouncy()),       .key(1.0),     ]     child:     ...

起初我对“关键帧(Keyframe)”这个词感到很困惑,不明白它的具体作用。实际上,它定义的是一种“多步动画”。

上面的代码定义了三个步骤:

  1. 1. 缩放比例从 1 降至 0.92
  2. 2. 从 0.92 升至 1.6
  3. 3. 再从 1.6 降回 1

在 Flutter 中,“frame(帧)”和“key(键/密钥)”这两个词都会引发强烈的特定联想。我认为如果将 Keyframe 替换为 Step(步骤),将 key 替换为 value(值),意思会更清晰。那么,上面的代码看起来就会像这样:

Actor(  acts: [    ScaleAct.stepped(      steps: Steps([        Step.value(0.92),        .value(1.6, motion: .bouncy()),        .value(1.0),      ]      child:      ...

不过我这纯属是在挑刺啦。😎

智能体技能(Agentic SKILL)这个插件的 API 并不简单,确实存在一定的学习曲线。但借助“Skills[7]”,我们现在就能上手。

事实上,那个示例代码并不是我亲手写的。倒不是因为看起来很难,纯粹是为了做个实验。首先,我创建了一个看起来长这样的页面:

image.png

起初只是一个包含三个 Text 组件的简单 Column

然后我要求智能体(Agent)使用 cue-animations 技能来为它制作动画。生成的代码正确率大约在 97% 左右。完全可行。

结语

Cue 是一个非常强大的动画插件包,它在设计复杂动画方面有着独特的方法论。

感谢阅读,祝你动画创作愉快!

往期阅读:

AnimatedCount:一款用于实现数字平滑动画的轻量级 Flutter 软件包

Flutter 3.38 动画新特性:动画引擎有什么新变化?

Flutter:我在网上看到了一个超炫的动画边框,于是我在 Flutter 里把它实现了出来

Flutter动画之Flare的制作与使用

Flutter 自定义 CustomPaint 实现流体液态加载动画

引用链接

[1] AnimatedX 系列组件,以及 `animate_do` 和 `flutter_animate` 插件: https://medium.com/easy-flutter/flutter-implicit-animations-with-and-without-packages-9292e0ba0330[2] `TweenAnimationBuilder`(补间动画构建器): https://medium.com/easy-flutter/flutter-implicit-animations-with-tweenanimationbuilder-1397c87b9e6e[3] 使用 `AnimationController` 的显式动画: https://medium.com/easy-flutter/flutter-explicit-animations-curves-vs-physics-vs-springs-41100beb8cb4[4] Cue: https://pub.dev/packages/cue[5] 文档: https://pub.dev/packages/cue#1-cue-the-trigger[6] `EzText`: https://medium.com/easy-flutter/flutter-my-new-widget-eztext-and-more-756104addb3c[7] Skills: https://github.com/Milad-Akarie/cue/blob/main/.github/skills/cue-animations/SKILL.md