Flutter跨页面切换时如何同时对多个Widget执行过渡动画
Flutter跨页面共享元素+同步入场动画实现方案
这种场景不需要做全页面硬替换,用Hero共享元素动画+路由级同步动画控制器的组合方案即可实现,既保证两个动画完全同步,也能避开第一页原有元素的遮挡问题。
核心实现思路
- 带文本的Container作为共享元素,用
Hero组件包裹后设置全局唯一的相同tag,Flutter会自动处理跨页面的位移动画,不需要手动写位移逻辑。 - 新增的TextField入场动画直接绑定页面路由的原生动画控制器,和Hero动画走同一个时间线,从根源上保证进度完全同步。
- 调整TextField动画的插值区间,让它在路由动画执行到一定阶段后再开始入场,刚好避开第一页原有元素的显示周期,解决遮挡问题。
具体实现代码
1. 封装共享Container组件
两个页面的Container复用同一个组件,外层统一套Hero:
class SharedTextContainer extends StatelessWidget { final String content; const SharedTextContainer({super.key, required this.content}); @override Widget build(BuildContext context) { return Hero( tag: 'shared_text_container', // 两个页面的tag必须完全一致 child: Container( width: 120, height: 40, alignment: Alignment.center, color: Colors.blue, child: Text(content, style: const TextStyle(color: Colors.white, fontSize: 14)), ), ); } }
2. 自定义页面路由,统一动画时长
用PageRouteBuilder定义第二页的路由,禁用默认页面过渡效果,统一动画时长:
PageRoute buildSecondRoute() { return PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const SecondPage(), transitionsBuilder: (context, animation, secondaryAnimation, child) => child, // 关闭默认过渡,完全走自定义动画 transitionDuration: const Duration(milliseconds: 600), // 全局统一动画时长 ); }
3. 第二页实现TextField同步入场动画
监听路由自带的动画控制器,绑定TextField的透明度+位移动画:
class SecondPage extends StatefulWidget { const SecondPage({super.key}); @override State<SecondPage> createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { Animation<double>? _textFieldAnim; @override void didChangeDependencies() { super.didChangeDependencies(); // 获取当前路由的原生动画控制器 final routeAnimation = ModalRoute.of(context)?.animation; if (routeAnimation != null && _textFieldAnim == null) { // 动画从路由进度30%开始执行,避开第一页元素遮挡 _textFieldAnim = Tween<double>(begin: 0, end: 1).animate( CurvedAnimation( parent: routeAnimation, curve: const Interval(0.3, 1.0, curve: Curves.easeInOut), ), ); } } @override Widget build(BuildContext context) { return Scaffold( body: ListView( children: [ // 侧边偏移的共享Container Padding( padding: const EdgeInsets.only(left: 30, top: 50), child: SharedTextContainer(content: '共享文本'), ), // 同步入场的TextField AnimatedBuilder( animation: _textFieldAnim!, builder: (context, child) { return Opacity( opacity: _textFieldAnim!.value, child: Transform.translate( // 配合淡入加轻微上滑效果,比单纯淡入更自然 offset: Offset(0, 20 * (1 - _textFieldAnim!.value)), child: const Padding( padding: EdgeInsets.all(20), child: TextField(decoration: InputDecoration(hintText: '请输入内容')), ), ), ); }, ), // 其他ListView元素 ], ), ); } }
问题排查
- 如果动画不同步:不要给TextField单独创建
AnimationController,必须绑定路由自带的动画控制器作为父动画。 - 如果仍然有遮挡:调整
Interval的起始值,把TextField入场时机延后到路由动画后半段即可。 - 如果需要自定义共享元素过渡效果:给
Hero组件传入flightShuttleBuilder参数,自定义位移过程中的中间态样式。
内容的提问来源于stack exchange,提问作者Sergey
相关产品推荐
相关产品推荐

