使用auto_route ^7.8.3的AutoTabsRouter在Shell路由间导航时Hero动画失效问题
问题分析
我之前碰到过类似的场景,Hero动画的核心逻辑是两个Hero组件必须处于同一个Navigator栈的上下文中,而且路由切换得是通过Navigator的push/pop这类标准栈操作触发的。你现在用AutoTabsRouter.of(context).navigate()切换Shell子路由时,本质是在同一个父路由下切换Tab页面,并没有触发Navigator的栈推送动作,Hero框架根本检测不到这种页面切换,自然不会启动动画。再加上你用了嵌套的AutoTabsRouter结构,两个Hero所在的页面可能处于不同的Navigator层级,进一步打乱了Hero的配对逻辑。
可能的解决方案
1. 改用标准Navigator推送跳转
如果FeedBrowseShellRoute不需要作为常驻Tab页,只是从FeedHomeShellRoute跳转过去的临时页面,可以试试用普通的Navigator推送代替AutoTabsRouter的navigate:
InkWell( onTap: () { Navigator.of(context).push( MaterialPageRoute(builder: (_) => FeedBrowseShellView()), ); }, child: Hero( tag: "hero_tag", child: // 你的子组件 ), )
不过这种方式会脱离AutoTabsRouter的管理,你得根据业务场景判断是否适用。
2. 调整路由结构,统一Navigator层级
把FeedHomeShellRoute和FeedBrowseShellRoute改成同级的普通AutoRoute,不再嵌套在FeedRoute的AutoTabs下,这样它们会共享同一个父Navigator栈,Hero动画就能正常触发。修改后的路由定义大概是这样:
@override List<AutoRoute> get routes => [ // 其他路由 AutoRoute( page: MainRoute.page, children: [ AutoRoute( initial: true, page: FeedRoute.page, ), AutoRoute(page: FeedBrowseShellRoute.page), AutoRoute(page: NewGenerationsRoute.page), AutoRoute(page: ProfileRoute.page), ], ), // 其他路由 ];
之后在FeedRoute里用context.pushRoute(const FeedBrowseShellRoute())跳转,Hero就能正常工作了。
3. 手动绑定HeroController(备选方案)
如果必须保留当前的嵌套AutoTabsRouter结构,可以尝试给嵌套的AutoTabsRouter手动提供HeroController,确保两个Tab页面的Hero共享同一个控制器:
AutoTabsRouter.tabBar( inheritNavigatorObservers: true, routes: const [FeedHomeShellRoute(), FeedBrowseShellRoute()], builder: (context, child, tabController) { return HeroControllerScope( controller: HeroController(), child: child, ); }, )
不过这种方式不一定能完全解决问题,毕竟Tab切换本质还是没触发Navigator栈变化,Hero可能还是感知不到。
4. 自定义Tab切换的Hero动画(兜底方案)
如果上面的方法都不适用,那就放弃原生Hero,手动实现类似的动画效果:
- 监听Tab切换事件
- 获取Hero子组件的全局坐标和尺寸
- 在切换过程中用Stack+Positioned组件模拟Hero的过渡动画
总结
最稳妥的方案是调整路由结构,让需要Hero动画的两个页面处于同一个Navigator栈中,用标准路由推送触发跳转。如果必须保留Tab结构,手动实现动画可能是更可靠的选择。
备注:内容来源于stack exchange,提问作者TecHaxter

