最近顺手整理了一个 Flutter 动画控件 Demo,文件
一开始只是想把业务里比较常见、也比较值得沉淀的几类动画控件放在一个页面里,方便自己后面回看。写完再回头看,感觉这类页面其实挺适合拿来梳理一个问题:
如果不是为了"做炫",而是为了做一套能在真实项目里长期复用的 Flutter 动画能力,到底哪些控件最值得优先沉淀?
这个问题我最近想得还挺多。
因为平时聊 Flutter 动画,大家很容易先想到的是 AnimationController、Tween、Curves、Hero,或者一些更偏效果层的展示。但放到业务里之后会发现,真正高频、真正影响体验、也真正值得花时间沉淀成基础能力的,往往不是那些看起来最复杂的动画,而是一些非常日常、几乎每个页面都会碰到的过渡和反馈。
这篇就想结合这个 Demo,整理一下我自己对"大厂 Flutter 动画控件方案"的一些理解。
先说结论:动画控件的重点,不是多,而是稳和高频 如果只从效果数量上看,动画这件事几乎是做不完的。今天可以做点击缩放,明天可以做粒子特效,后天还能接 Lottie、Rive、甚至更复杂的 shader 表现。
但真放到业务里,其实不会这么选。
更现实的思路通常是:优先沉淀那些覆盖率高、复用率高、能明显改善体验,同时维护成本又可控的控件。
从这个角度看,我现在更愿意把业务动画控件理解成一套"高频体验组件",而不是一组"视觉效果组件"。
这个 Demo 里,我最后留下来的就是 6 类:
- 点击反馈
- 状态过渡
- 通知条
- 展开收起
- 底部弹层
- Hero 共享元素转场
回头看,它们几乎都不是为了"展示技巧",而是为了回答一个很实际的问题:用户操作之后,页面是不是更自然了一点。
1. 点击反馈控件,是最小但最不能缺的一层 如果让我只保留一类动画能力,我大概率会先保留点击反馈。
原因很简单,用户和界面的关系,本质上是"我做了一个动作,系统有没有回应我"。这个回应不一定非要很重,有时候只是按下时轻微缩放一下,抬手时恢复原位,配一点阴影变化,就已经足够建立确认感了。
在 Demo 里,这部分对应的是 _PressFeedbackCard。实现方式也很轻:AnimatedScale + AnimatedContainer。
我一直觉得,这类动画的价值不在于"看起来高级",而在于它把触摸和视觉反馈真正对上了。页面有没有这种基础反馈,用户的体感差异会非常明显。
而且这类控件很适合沉淀。因为按钮、卡片、列表项、运营入口、Tab 卡片,这些地方都会用到。与其每个页面都自己写一遍按下态,不如统一做一个轻量的 Pressable 能力,把时长、曲线、缩放比例统一起来。
这种事情做完之后,整个 App 的交互质感会一下子整齐很多。
2. 状态过渡控件,很多时候比性能优化更先被用户感知 这次 Demo 里,我自己比较喜欢的一块是状态切换。
loading / content / empty / error 这几个状态,其实几乎所有内容型产品、商城、搜索页、列表页都会碰到。用户每天都在看到它们,但很多页面处理得很直接:要么整个页面突然变一下,要么切换时闪一下,要么高度跳动得很明显。
这类问题不一定会被归类为"动画问题",但用户感知到的那种"不顺",很多时候就是从这里来的。
所以我现在会觉得,状态过渡是非常值得优先做成基础控件的一类能力。
在这个 Demo 里,我用了 AnimatedSwitcher,再叠了一层 FadeTransition + SizeTransition。这套组合不复杂,但非常实用:既能让不同状态之间切得更柔和一点,也不会为了追求效果把逻辑搞得太重。
后面还顺手给 Content 状态下的 LinearProgressIndicator 增加了一个 0 -> 0.9 的入场动画。这个小改动其实挺符合业务直觉的。很多时候真实页面不需要大开大合的动画,恰恰是这种很小的动态信号,会让内容"活一点"。
我会觉得,这类控件做得好的价值不只是界面更顺,而是它能把状态变化变成一种可感知但不刺眼的体验。
3. 通知条控件,重点是"别打断用户" 通知条是我觉得特别有代表性的一类业务动画。
运营提醒、活动提示、网络恢复通知、优惠券提醒,这些东西几乎每个产品都会有。但这类信息本身有一个天然矛盾:它要被看见,但又不能太打断。
所以这类控件的核心,不是"怎么出现得更炫",而是"怎么出现得合理"。
Demo 里这部分用的是 AnimatedSlide + AnimatedOpacity。我一直挺喜欢这种组合,因为它很符合人对"出现"和"消失"的直觉:有一个明确方向感,同时又不会太生硬。
这种控件沉淀下来的价值很高。因为它一旦标准化,后面无论是活动通知、价格变动提醒,还是局部系统提示,都可以走同一套交互语言。用户不会每次都遇到一种新的弹法,整个产品也更统一。
4. 展开收起控件,看起来简单,其实特别适合沉淀 展开收起是另一类很容易被忽视,但其实很高频的能力。
商品详情、FAQ、评论全文、权益说明、折叠介绍文案,这些场景都离不开"默认只展示一部分,点击之后自然展开"的交互。很多时候如果没有过渡,页面会显得很硬;如果过渡太重,又会拖慢节奏。
所以它特别适合走一条中间路线:够轻,够自然,够稳定。
我在 Demo 里用的是 AnimatedSize,配合高度约束做展开和收起。这种做法我自己比较偏爱,因为它没有那么多显式状态管理的负担,但又足够表达"内容正在展开"这件事。
如果后面继续往业务里抽,我会很愿意把它做成一种通用的 ExpandableText 或 ExpandableSection。因为这类控件的复用频率真的很高,而且用户对它的预期也很稳定:我点一下,它展开;我再点一下,它收起;整个过程别太突兀。
5. 底部弹层控件,最需要统一的是规范 相比前面几类控件,底部弹层更像一种"平台能力"。
你会发现业务里几乎什么都能往 bottom sheet 里装:更多操作、筛选面板、分享、举报、支付确认、说明弹层、快捷操作。问题也恰恰在这里,如果不统一,整个项目里的弹层体验会特别散。
有的弹得快,有的弹得慢。
有的圆角不一样,有的拖拽行为不一样。
有的会遮住安全区,有的返回逻辑还不一致。
所以底部弹层这类控件真正值得沉淀的,不是单个页面里的实现,而是统一入口和统一规则。
在 Demo 里我先做了一个基础版的操作面板,使用的是 showModalBottomSheet。这一步其实更多是在明确"业务高频动作面板应该长什么样"。如果后面继续往前走,我会更倾向于把它抽成统一 route 或 host,把以下这些东西都收起来:
- 圆角样式
- 动画速度
- 拖拽关闭行为
- SafeArea 处理
- 埋点
- 降级逻辑
当这些被统一托管之后,弹层才真正从"一个控件"变成"一个能力"。
6. Hero 共享元素转场,最大的价值是保留空间上下文 Hero 这种能力,第一眼看上去很容易让人把注意力放在"转场很丝滑"上。但如果从业务体验去看,它最有价值的地方,其实是保留空间上下文。
比如列表卡片进入详情页,用户会天然希望知道:我是从哪张卡片进来的。
如果页面一切,内容突然全屏展开,虽然功能没问题,但空间连续性会断掉。
而 Hero 能把这件事接起来,让用户知道这两个页面之间是连续的。
这类过渡在内容流、商城、相册、个人主页这些场景里都很常见,而且投入产出比很高。因为 Flutter 原生就提供了 Hero,只要规范做好,基本可以用相对低的成本做出不错的效果。
当然,真放进项目里,也不是写个 tag 就结束了。真实使用时通常还要补齐一些规则:
- tag 的命名方式
- 同页唯一性
- 复杂 child 的包裹方式
- 不适合做 Hero 时的降级策略
这些细节平时看不出来,但一旦页面多起来,如果没有规范,Hero 往往也是最容易出现"局部很好看,全局很混乱"的那类能力。
如果把这些控件再往前推一步,它们就不只是 Demo 了 写完这个页面之后,我一个比较强烈的感受是:这些控件看起来像是几个独立小功能,但如果继续往前推,其实很自然就会长成一整套动画能力层。
大概会有几个明确方向。
第一个方向是 token 化。
也就是把动画时长、曲线、延迟统一抽出来,不让每个页面自己写 200ms、300ms、easeInOut。这种统一并不会让动画更炫,但会让全局体验更整齐。
第二个方向是组件化。
把现在页面里的这些能力抽成真正可复用的基础组件,比如:
AppPressableAppStateSwitcherAppNoticeBannerAppExpandableSectionAppActionSheetAppHeroCard
这样业务页面只负责组合,不再重复写动画细节。
第三个方向是降级。
这点其实很关键。动画一旦进入真实项目,就必须考虑低端机、长列表、首屏路径、系统减少动态效果等场景。也就是说,动画能力不只是"能做",还得"能关、能减弱、能在关键链路里让位"。
第四个方向是可观测。
如果真的把动画当成产品体验的一部分,那它就不应该只是视觉感受,还应该可量化。比如路由切换耗时、jank、帧率波动、关键动画链路的中断率,这些东西最终都会决定动画是不是只停留在演示层。
我现在越来越觉得,动画控件的价值不在于展示,而在于统一体验 回头看这次 Demo,我觉得最大的收获其实不是"又做了几种动画",而是更清楚地感觉到,Flutter 动画在业务里真正值得沉淀的部分,几乎都和统一体验有关。
点击反馈是在统一交互确认。
状态切换是在统一页面过渡。
通知条是在统一局部提醒。
展开收起是在统一信息层级变化。
底部弹层是在统一操作面板风格。
Hero 是在统一页面空间连续性。
它们共同解决的,其实不是"好不好看",而是"顺不顺、稳不稳、是不是同一种语言"。
这也是我现在比较愿意做这类 Demo 的原因。因为只有把这些能力放到一个页面里对着看,才会更明显地感觉到:动画从来不是孤立的小技巧,它本质上是交互系统的一部分。
最后 如果只把 Flutter 动画理解成几个 API,学起来当然也没问题。但如果真的放到长期维护的项目里去看,动画更像是一套体验能力,需要从高频场景里往外长,而不是从特效里往里收。
所以对我来说,这个页面更像一个起点。它不是为了把所有动画都做一遍,而是为了先回答一个更实际的问题:
哪些动画控件,是真的值得优先沉淀下来的。
现阶段我的答案,大概就是这 6 类。
文末信息