最近顺手整理了一个 Flutter 动画 Demo,代码放在
一开始的想法其实很简单,就是把平时常见的几类动画能力串一下,做一个方便自己回看的小页面:隐式动画、显式动画、交织动画、AnimatedWidget、Hero,再顺手补一点自定义曲线和自绘的内容。
结果写着写着发现,这种 Demo 还挺适合拿来复盘面试题的。
因为 Flutter 动画这件事,表面上看是在用几个组件,实际上考得往往不只是"会不会写一个 AnimatedContainer"。面试里更常见的问题 usually 是:
- 动画是谁在驱动的
Tween、Curve、AnimationController分别在做什么- 隐式动画和显式动画怎么选
AnimatedBuilder和AnimatedWidget差别在哪里- Hero 为什么能跨页面飞起来
- 自定义动画为什么容易在边界条件上踩坑
这篇不想写成一份很标准的教程,更像是一次实践之后的整理记录。
先别急着记组件,先理解动画到底是什么 刚接触 Flutter 动画时,很容易把注意力放在组件名上。
比如:
AnimatedOpacity用来做透明度过渡AnimatedContainer可以做尺寸和颜色变化Hero可以做共享元素转场AnimationController能手动控制进度
这些都没错,但如果只停在"组件怎么用",其实很容易学散。
我现在更愿意把 Flutter 动画理解成三层:
- 时间是谁提供的
- 数值怎么变化
- 变化最终怎么映射到界面上
时间这一层,核心是 Ticker 和 AnimationController。
数值变化这一层,是 Tween、Curve、CurvedAnimation、Interval。
界面呈现这一层,则可能是 Transform、Opacity、Container、CustomPainter,也可能是 Hero 这种页面级过渡能力。
这么看之后,很多动画 API 会突然变得顺起来。它们不是一堆互不相干的组件,而是在不同层次上分工协作。
隐式动画:适合"我只想让它自然过渡" 这个 Demo 里,我专门留了一块隐式动画区域,用了 AnimatedContainer、AnimatedOpacity、AnimatedAlign、AnimatedDefaultTextStyle。
隐式动画的体验很好,原因也很直接:你不太需要关心控制器,状态一改,Flutter 会帮你把旧值平滑过渡到新值。
这类写法很适合下面这些场景:
- 卡片展开和收起
- 按钮高亮态切换
- 小元素在两个位置之间移动
- 文本字号、颜色的轻量变化
它最大的价值不是"功能弱",而是"足够省心"。
从原理上说,隐式动画并不是没有控制器,而是框架已经把控制器和插值逻辑帮你封装好了。你看到的是属性变化,框架内部做的是补间和驱动。
所以如果面试里被问到"隐式动画和显式动画怎么选",我现在比较习惯这样回答:
如果我只关心结果,不太关心中间过程,也不需要暂停、反向、重复、监听进度,那么隐式动画通常是第一选择。代码短,心智负担低,也更适合日常业务页面里的轻量交互。
显式动画:真正开始接触 Flutter 动画骨架的地方 显式动画这一块,是我觉得 Flutter 动画最值得认真理解的部分。
在 Demo 里,我做了一组比较典型的交织动画:缩放、旋转、位移、变色和透明度变化,共用一个 AnimationController,再通过不同的 Tween 和 Interval 切分成各自的节奏。
这个思路非常像搭一条统一时间轴。
AnimationController 提供的是一个从 0.0 到 1.0 的全局进度。
然后你可以为不同属性定义各自的映射方式:
- 缩放从
0.5 -> 1.5 - 旋转从
0 -> 1圈 - 位移从
-100 -> 100 - 颜色从蓝色变成粉色
- 透明度从半透明变成完全显示
如果所有属性都跟着同一个线性节奏一起走,动画会显得很平。
所以这里真正有意思的地方,是 CurvedAnimation + Interval。
Curve 决定节奏感。
Interval 决定某个属性在整条时间轴里的哪个区间开始、哪个区间结束。
这也是交织动画的关键:不是多个动画分开跑,而是多个属性共享同一条时间轴,然后各自在不同时间段生效。
这个思路一旦理解了,后面很多复杂动画其实都只是参数和节奏的排列组合。
AnimatedBuilder 和 AnimatedWidget,我现在更愿意从"工程感"去区分 这两个 API 很容易一起被问到。
如果只看表面,它们都可以监听动画,然后在动画变化时刷新局部 UI。
但如果放到实际项目里去理解,我觉得它们最本质的差别不在"能不能做动画",而在"动画逻辑放在哪里"。
AnimatedBuilder 更适合页面内一次性的动画逻辑。
优点是直接,写在调用处,读起来集中。
如果只是某个页面局部做一个缩放、透明度或旋转效果,它很好用。
AnimatedWidget 则更适合做成独立组件。
比如 Demo 里的脉冲加载、心跳按钮、旋转圆环进度,这些都更像通用能力。把它们封装成一个独立 widget,后面在别的页面里也能直接复用。
所以如果让我用一句更口语的话总结:
AnimatedBuilder 更像"在当前页面里写一段动画"。
AnimatedWidget 更像"做一个可复用的动画组件"。
这两者没有绝对高低,只是抽象层次不同。
自定义 Curve 之后,我反而更能理解动画的边界问题 这次 Demo 里,我顺手做了一个心跳按钮,想模拟那种点赞时"跳两下再回落"的感觉,所以写了一个自定义 _HeartbeatCurve。
这类需求很常见。系统内置曲线已经覆盖了大多数场景,但有些业务动作就是有自己的节奏语义。比如点赞、脉冲提醒、轻微回弹,它们通常不是单一的 easeInOut 就能表达出来的。
写这段曲线的时候,我有一个很直观的感受:
Curve 本质上不是"动画效果",而是一个函数。
它做的事,是把线性的时间进度重新映射成另一条节奏曲线。
理解这一点之后,再看 easeIn、easeOut、elasticOut,会更容易把它们当成不同的时间函数,而不是"某个固定效果"。
不过这次也顺手踩了一个坑。
最开始我把心跳曲线直接拿去配 CurveTween 用,结果在动画终点附近触发了断言。往下看之后发现,本质上不是 Flutter "抽风",而是我把一种更偏"数值幅度"的曲线,当成了标准时间曲线去使用。
这个细节其实挺有意思。因为它说明动画里很多问题,不是 API 会不会用,而是你有没有理解这个 API 期待的输入语义。
我现在会觉得,这类踩坑经验比单纯记住某个类名更有价值。因为一旦真的理解了,后面写自定义动画时会稳很多。
Hero 动画,重点不是"同 tag 就能飞" Hero 也是 Flutter 动画里很高频的一类能力。
第一次接触的时候会觉得很神奇:两个页面上只要有相同 tag 的 Hero,跳转时元素就会自动飞起来,几乎不用你额外管太多。
但如果只把 Hero 理解成"自动转场",其实还是有点浅。
更准确一点说,Hero 是共享元素过渡。
在路由切换过程中,Flutter 会找到源页面和目标页面中匹配的 Hero,把过渡过程交给更上层统一管理,让这个元素从一个页面的位置、尺寸和样式,平滑过渡到另一个页面。
所以 Hero 真正有趣的点,不在于"它会飞",而在于它本质上已经不是普通的页面内部动画,而是一种跨路由的共享元素动画机制。
Demo 里我还加了 flightShuttleBuilder,给飞行中的元素附带了一点旋转效果。这个地方挺适合在面试里展开讲,因为它能说明 Hero 并不只是一个默认黑盒,你还可以接管飞行中的表现。
另外,Hero 也有几个很常见的小坑:
tag必须能一一对应- 同一个页面里不要重复使用相同
tag - child 比较复杂时,最好包一层
Material,避免转场中样式不一致
这些都不算难,但属于"做过的人会自然提到"的细节。
动画真正难的,经常不是做出来,而是跑得稳 我现在越来越觉得,动画这块最能体现经验差异的地方,不是效果有多花,而是复杂情况下还能不能稳定。
比如下面这些点,平时看着很基础,但实际很重要:
AnimationController要放在合适的生命周期里创建和释放- 能局部刷新就不要让整页跟着频繁 rebuild
- 自绘场景要认真处理
shouldRepaint - 自定义曲线和区间映射要注意边界值
- 反向播放、重复播放、连续点击切换状态时,最容易暴露问题
尤其是动画相关问题,一旦遇到边界情况,经常不是"看起来不太顺",而是直接抛断言、闪烁或者节奏错乱。
所以如果让我现在再概括一次动画优化,我会更愿意说:
动画优化并不是让它更炫,而是让它在复杂条件下依然稳定、顺滑、可控。
回到面试,我觉得真正想讲清楚的是这几件事 如果只是为了应对面试,这个 Demo 对我最大的帮助,不是让我又多记住了几个组件,而是让我把 Flutter 动画系统重新串了一遍。
串完之后,很多回答会自然很多。
比如:
- 隐式动画适合简单状态过渡,优先考虑开发效率
- 显式动画适合需要精细控制时间轴和生命周期的场景
- 交织动画的关键是一个控制器配多个属性,再用
Interval切阶段 AnimatedBuilder和AnimatedWidget的差别主要体现在复用和封装方式上- Hero 的本质是共享元素在路由切换中的过渡
- 自定义 Curve 不只是"效果更炫",更是在表达业务节奏
- 真正有经验的动画实现,最后还是会落到性能、边界和稳定性上
这些内容单拆开都不复杂,但只有放到一个完整 Demo 里跑一遍,才会真正连起来。
最后 这次整理 Flutter 动画 Demo,某种程度上也算是给自己做了一次小复盘。
越往后越觉得,动画不是一个孤立知识点。它其实把 Flutter 里不少核心能力都串起来了:调度、状态变化、Widget 更新、局部刷新、自绘、页面转场,还有性能取舍。
如果只是把效果做出来,其实并不难。
但如果想把它讲清楚,尤其是在面试里讲得不空、不散,我觉得最有效的方式,还是自己动手做一个最小可运行 Demo,然后把里面的原理、取舍和踩坑真正走一遍。
对我自己来说,这种方式比单纯背题更扎实一些。