Flutter Material Motion 预置动画指南:animations 包四大过渡模式与实战集成
2026/9/18 0:29:52 网站建设 项目流程

Flutter Material Motion 预置动画指南:animations 包四大过渡模式与实战集成

【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages

导读

本文围绕 Flutter 官方维护的animations包展开,系统讲解 Material motion 体系中的四大过渡模式:Container transform(容器变换)、Shared axis(共享轴)、Fade through(淡入淡出切换)与 Fade(淡入)。你将学会通过OpenContainerSharedAxisTransitionFadeThroughTransitionFadeScaleTransition等开箱即用的组件,把经过设计验证的高质量转场动画直接嵌入自己的 Flutter 应用,并了解PageTransitionSwitchershowModal等底层基础设施的用法与实现原理。

animations是 Flutter 团队维护的开源包(当前仓库中版本为 3.0.0,SDK 约束^3.12.0、Flutter>=3.44.0,依赖material_ui),它提供了一系列"开箱即用"(pre-canned)的高质量动画,允许你用自定义内容替换动画载体后直接落入应用,以取悦用户并提升交互质感。

一、Material motion:为何需要统一的转场模式

Material motion 是一组帮助用户理解和导航应用的过渡模式。其设计哲学是:转场动画不是装饰,而是传达"元素之间如何关联"的信息。animations包把 Material motion 规范的四个核心模式固化为可直接复用的 Flutter 组件:

  1. Container transform—— 用于包含容器的 UI 元素之间的转场,在两个元素之间建立可见的联系;
  2. Shared axis—— 用于具有空间或导航关系的 UI 元素之间的转场,通过在 x、y 或 z 轴上施加共享变换强化元素间的关系;
  3. Fade through—— 用于彼此没有强关联的 UI 元素之间的转场;
  4. Fade—— 用于在屏幕边界内进入或退出的 UI 元素(如对话框、菜单、Snackbar、FAB)。

选择哪种模式的核心依据是元素之间的语义关系强度:从强关联(容器变换)到弱关联(fade through)再到无关联(fade),这构成了 Material motion 的完整过渡光谱。

二、快速体验:运行官方演示应用

在真机或模拟器上查看上述动画的实际效果,可进入包自带的示例工程:

cd packages/animations/example/ flutter run --release

示例应用入口在 packages/animations/example/lib/main.dart:主页以列表形式列出了四种过渡模式(Container transform / Shared axis / Fade through / Fade),并提供一个Slow animations开关,通过timeDilation = 20.0把动画放慢 20 倍,方便逐帧观察动画曲线与元素运动细节。

三、Container transform:OpenContainer

Container transform模式专为"包含容器"的 UI 元素之间的转场设计,例如:

  1. 卡片展开为详情页;
  2. 列表项展开为详情页;
  3. FAB 展开为详情页;
  4. 搜索栏展开为全屏搜索。

其核心组件是 OpenContainer,一个"点击后放大填满屏幕以揭示新内容"的容器:关闭状态下显示closedBuilder构建的 Widget;点击后容器平滑放大至整个Navigator尺寸,同时关闭态内容淡出、打开态内容淡入;通过openBuilder提供的回调或 Android 返回键关闭时动画反向播放。

3.1 最小示例

OpenContainer( transitionDuration: const Duration(milliseconds: 500), transitionType: ContainerTransitionType.fadeThrough, openBuilder: (context, action) { return Scaffold( appBar: AppBar(title: const Text('Details Page')), body: const Center( child: Text( 'This page opened with Container Transform animation', style: TextStyle(fontSize: 18), textAlign: TextAlign.center, ), ), ); }, closedBuilder: (context, action) { return Container( width: 200, height: 120, alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(16), ), child: const Text( 'Open Details', style: TextStyle(color: Colors.white, fontSize: 18), ), ); }, ),

以上示例代码直接取自 open_container.dart 的文档注释,展示了"蓝色圆角卡片 → 全屏详情页"的经典用法。

3.2 完整参数说明

参数类型默认值说明
closedColorColorColors.white容器关闭时的背景色
openColorColorColors.white容器打开时的背景色
middleColorColor?主题canvasColorfadeThrough转场类型使用的中间过渡色
closedElevationdouble1.0关闭时容器的海拔高度
openElevationdouble4.0打开时容器的海拔高度
closedShapeShapeBorder圆角 4.0 的RoundedRectangleBorder关闭时容器形状
openShapeShapeBorder矩形RoundedRectangleBorder()打开时容器形状
onClosedClosedCallback<T?>null容器关闭(pop)时回调,接收 pop 返回的数据;未返回值时为null
closedBuilderCloseContainerBuilder必填构建关闭状态内容;可通过action回调打开容器
openBuilderOpenContainerBuilder<T>必填构建打开状态内容;可通过action回调关闭容器
tappablebooltrue是否允许点击整个关闭态容器打开;设为false时只能通过closedBuilderaction回调打开
transitionDurationDuration300ms开/关转换动画时长
transitionTypeContainerTransitionTypefade新旧内容的淡入淡出方式
useRootNavigatorboolfalse路由推入最近的还是最远的Navigator
routeSettingsRouteSettings?null提供给新路由的附加数据
clipBehaviorClipClip.antiAlias关闭态内容的裁剪方式
closedShadows/openShadowsList<BoxShadow>?null自定义阴影;提供后对应的closedElevation/openElevation将被忽略

3.3 实现原理

从源码看,OpenContainerState.openContainer()会构造一个私有的_OpenContainerRoute推入Navigator,动画期间新旧内容同时存在于 Widget 树中,因此两个 builder 返回的 Widget 不能使用相同的 GlobalKey(open_container.dart)。

转场的核心是一组 Tween:

  • RectTween驱动容器从关闭位置/尺寸(_rectTween.begin)平滑放大到整个 Navigator 尺寸(_rectTween.end);
  • ShapeBorderTween驱动closedShapeopenShape的形状变换;
  • ColorTween_getColorTween)与透明度 Tween(_getClosedOpacityTween/_getOpenOpacityTween)根据ContainerTransitionType生成不同权重的TweenSequence
    • fade模式:关闭态内容保持不透明,打开态内容在动画后 1/5 时间段内淡入,同时背景色从closedColor渐变到openColor
    • fadeThrough模式:关闭态内容先淡出(前 1/5 时间段),背景色经由middleColor过渡,打开态内容随后淡入。

所有动画统一施加Curves.fastOutSlowIn曲线,并通过_FlippableTweenSequence实现打开/关闭动画的精确反向回放;中断转场(如动画进行中再次点击)由_transitionWasInterrupted检测并处理。

四、Shared axis:共享轴过渡

Shared axis模式用于具有空间或导航关系的元素,通过在 x、y、z 轴上共享同一变换方向,强化"上一屏与下一屏位于同一空间"的感知:

  1. 引导页流程沿 x 轴过渡;
  2. 分步器(stepper)沿 y 轴过渡;
  3. 父子级导航沿 z 轴过渡。

4.1 两种接入方式

方式一:作为全局页面转场。通过SharedAxisPageTransitionsBuilder挂到PageTransitionsTheme中,替换所有MaterialPageRoute的默认转场(示例取自 shared_axis_transition.dart):

MaterialApp( theme: ThemeData( pageTransitionsTheme: PageTransitionsTheme( builders: { TargetPlatform.android: SharedAxisPageTransitionsBuilder( transitionType: SharedAxisTransitionType.horizontal, ), TargetPlatform.iOS: SharedAxisPageTransitionsBuilder( transitionType: SharedAxisTransitionType.horizontal, ), }, ), ), // routes: ... );

方式二:作为局部内容切换。在PageTransitionSwitchertransitionBuilder中直接使用SharedAxisTransition(示例取自 shared_axis_transition.dart):

PageTransitionSwitcher( transitionBuilder: ( Widget child, Animation<double> primaryAnimation, Animation<double> secondaryAnimation, ) { return SharedAxisTransition( animation: primaryAnimation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.horizontal, child: child, ); }, child: Container( key: ValueKey<int>(_selectedIndex), // 切换 key 才能触发过渡 color: _colors[_selectedIndex], child: const Center(child: FlutterLogo(size: 300)), ), ),

4.2 轴类型与实现细节

SharedAxisTransitionType枚举定义三种轴:

  • vertical—— 沿 y 轴垂直过渡,适合注册/登录分步流程;
  • horizontal—— 沿 x 轴水平过渡,适合向导、轮播类页面;
  • scaled—— 沿 z 轴缩放过渡,适合父子级导航(如点击进入详情)。

从 shared_axis_transition.dart 的实现可以看到其动画编排:进入元素使用淡入(_fadeInTransition,在 0.3~1.0 区间内完成)配合 30 像素位移(x/y 轴)或从 0.80 缩放到 1.00(z 轴);退出元素使用淡出(_fadeOutTransition,在 0.0~0.3 区间内完成)配合反向位移或缩放到 1.10,并以fillColor(默认主题canvasColor)作为过渡期间的背景色,保证位移露出的区域颜色一致。通过DualTransitionBuilder分别驱动前进(forwardBuilder)与后退(reverseBuilder)方向的动画。

五、Fade through:淡入淡出切换

Fade through模式用于彼此没有强关联的 UI 元素之间的切换,典型场景:

  1. 点击底部导航栏的不同目的地;
  2. 点击刷新图标;
  3. 切换账号。

它的特征与 Shared axis 一样是"先出后进":旧元素淡出,然后新元素淡入并从 0.92 放大到 1.0。缩放仅施加在进入的元素上,以强调新内容优先于旧内容。

5.1 作为页面转场

FadeThroughPageTransitionsBuilder同样挂载到PageTransitionsTheme(示例取自 fade_through_transition.dart):

MaterialApp( theme: ThemeData( pageTransitionsTheme: PageTransitionsTheme( builders: { TargetPlatform.android: FadeThroughPageTransitionsBuilder(), TargetPlatform.iOS: FadeThroughPageTransitionsBuilder(), }, ), ), // routes: ... );

5.2 与 PageTransitionSwitcher 组合

PageTransitionSwitcher.transitionBuilder中使用FadeThroughTransition,即可为底部导航等场景的视图切换注入 fade through 动画(示例取自 fade_through_transition.dart):

PageTransitionSwitcher( transitionBuilder: ( Widget child, Animation<double> primaryAnimation, Animation<double> secondaryAnimation, ) { return FadeThroughTransition( child: child, animation: primaryAnimation, secondaryAnimation: secondaryAnimation, ); }, child: Container( key: ValueKey<int>(_selectedIndex), // 每次切换必须更换 key color: _colors[_selectedIndex], ), bottomNavigationBar: BottomNavigationBar( // items、currentIndex、onTap ... ), ),

5.3 实现细节

源码_ZoomedFadeIn/_FadeOut(fade_through_transition.dart)用两个TweenSequence精确控制时序:淡入阶段在前 6/20 时间段内完全不透明(透明度恒为 0、缩放恒为 0.92),后 14/20 时间段内按Cubic(0.0, 0.0, 0.2, 1.0)曲线完成淡入与放大;淡出阶段在前 6/20 时间段内按Cubic(0.4, 0.0, 1.0, 1.0)曲线淡出,其余时间保持完全透明。两段动画"错峰"进行,从而形成 fadethrough的视觉节奏。

六、Fade:淡入过渡(FadeScaleTransition 与 showModal)

Fade模式用于在屏幕边界内进入或退出的 UI 元素,例如:

  1. 对话框;
  2. 菜单;
  3. Snackbar;
  4. FAB。

实现上,进入的元素快速淡入并从 80% 缩放到 100%,退出的元素仅淡出——缩放同样只作用于进入元素。注意FadeScaleTransition不同于 Flutter 自带的FadeTransition(后者只动画化子 Widget 的透明度)。

6.1 在普通场景使用

FadeScaleTransition( animation: animation, // 通常来自 AnimationController child: child, )

FadeScaleTransition通过DualTransitionBuilder组合两段动画(fade_scale_transition.dart):前进方向使用Interval(0.0, 0.3)淡入 + 0.80→1.00 缩放(Easing.legacyDecelerate曲线),后退方向使用 1.0→0.0 的透明度渐变。

6.2 与 showModal 组合:弹窗/菜单

animations包还提供showModalAPI 与FadeScaleTransitionConfiguration,将 fade 模式应用于模态弹窗(示例取自 fade_scale_transition.dart):

showModal( context: context, configuration: FadeScaleTransitionConfiguration(), builder: (BuildContext context) { return Center( child: SizedBox( width: 250, height: 250, child: const Material( child: Center(child: FlutterLogo(size: 250)), ), ), ); }, );

FadeScaleTransitionConfiguration继承自ModalConfiguration,可配置参数(见 fade_scale_transition.dart):

参数默认值说明
barrierColorColors.black54遮罩(scrim)颜色
barrierDismissibletrue点击遮罩是否关闭弹窗
transitionDuration150ms进入动画时长
reverseTransitionDuration75ms退出动画时长
barrierLabel'Dismiss'遮罩的无障碍语义标签

从 modal.dart 的实现可见,showModal内部通过Navigator.push推入一个PopupRoute_ModalRoute),其useRootNavigator默认为true——即模态默认推入根 Navigator。因此,当应用存在多层Navigator时,关闭弹窗应使用Navigator.of(context, rootNavigator: true).pop(result)而非普通的Navigator.popshowModal返回Future<T?>,关闭时传入Navigator.pop的值即为该 Future 的解析结果。

七、PageTransitionSwitcher:模式化的 AnimatedSwitcher

PageTransitionSwitcher是上述各过渡模式共享的基础设施(page_transition_switcher.dart)。它是AnimatedSwitcher的变体,区别在于:AnimatedSwitcher对进入/退出使用同一套动画,而PageTransitionSwitcher允许像PageRoute一样分别指定进入转场(primaryAnimation)与退出转场(secondaryAnimation)

关键行为:

  • child更换时,transitionBuilder会被应用到新旧两个 child 上:新 child 的primaryAnimation向前播放(定义其"出现"),旧 child 的secondaryAnimation向前播放(定义其"消失")——相当于 push 一个新PageRoute
  • reverse: true时行为反转,相当于 pop 一个PageRoute
  • 切换必须更换 child 的Key:若新旧 child 类型与 key 相同,框架认为它们是同一个 Widget 而直接更新参数,不会触发过渡。通常使用ValueKey区分(如ValueKey<int>(_selectedIndex));
  • 快速连续切换时,多个旧 child 可以同时在树上淡出,新 child 淡入;
  • layoutBuilder控制新旧 child 的布局,默认defaultLayoutBuilder将所有条目放入居中对齐的Stack,也可自定义(如左上角对齐)。

八、库结构速览

animations包的全部导出集中在 lib/animations.dart,六个实现文件一一对应四种模式及两个基础设施:

文件对应模式 / 组件
lib/src/open_container.dartContainer transform:OpenContainerContainerTransitionType
lib/src/shared_axis_transition.dartShared axis:SharedAxisTransitionSharedAxisPageTransitionsBuilderSharedAxisTransitionType
lib/src/fade_through_transition.dartFade through:FadeThroughTransitionFadeThroughPageTransitionsBuilder
lib/src/fade_scale_transition.dartFade:FadeScaleTransitionFadeScaleTransitionConfiguration
lib/src/modal.dart模态弹窗基础设施:showModalModalConfiguration
lib/src/page_transition_switcher.dartPageTransitionSwitcher(通用内容切换器)

对应的单元测试覆盖了各模式的核心行为(test 目录):fade_scale_transition_test.dartfade_through_transition_test.dartshared_axis_transition_test.dartopen_container_test.dartmodal_test.dartpage_transition_switcher_test.dart以及dual_transition_builder_test.dart,可作为理解动画时序与中断处理行为的补充参考。

九、集成到自己的应用

pubspec.yaml中声明依赖后即可使用:

dependencies: animations: ^3.0.0

接入路径归纳如下:

  1. 页面级转场:在ThemeData.pageTransitionsTheme中按平台配置SharedAxisPageTransitionsBuilderFadeThroughPageTransitionsBuilder,一键替换所有MaterialPageRoute的转场;
  2. 局部内容切换:用PageTransitionSwitcher+SharedAxisTransition/FadeThroughTransition,为底部导航、Tab 内容区等注入专业转场;
  3. 容器展开:用OpenContainer实现卡片/列表项/FAB/搜索栏到详情页的放大过渡;
  4. 弹窗与浮层:用showModal+FadeScaleTransitionConfiguration获得符合 Material 规范的淡入弹窗。

结语

animations包把 Material motion 规范中"如何选择过渡模式"的语义判断,物化为四个可直接使用的组件和一套统一的切换基础设施。使用它时,核心决策在于根据元素间的关系强度选择模式:强关联用 Container transform,空间/导航关联用 Shared axis,弱关联用 Fade through,屏幕内进出用 Fade。在此基础上,通过各组件暴露的颜色、形状、海拔、时长与曲线参数,即可将默认动画调整为符合应用视觉语言的自定义转场。

【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询