如何在GoRouter中实现多场景下的路由模块复用?
GoRouter 复用路由与循环导航问题的解决方案
针对模块多位置复用的问题
你可以把每个模块的路由结构封装成可复用的路由生成函数,通过传入不同前缀生成唯一路由名称,同时完全复用页面组件和子路由逻辑:
- 先定义模块的基础路由配置函数,比如Schedule模块:
List<GoRoute> buildScheduleRoutes(String prefix) { return [ GoRoute( name: '$prefix-schedule', path: '$prefix/schedule', builder: (context, state) => const SchedulePage(), routes: [ // 复用子路由 GoRoute( name: '$prefix-schedule-detail', path: 'detail/:id', builder: (context, state) => ScheduleDetailPage(id: state.pathParameters['id']!), ), ], ), ]; }
- 然后在BottomNavigationBar和More菜单的路由中分别调用该函数,传入不同前缀:
final router = GoRouter( routes: [ // BottomNavigationBar 路由组 ShellRoute( builder: (context, state, child) => BottomNavPage(child: child), routes: [ ...buildScheduleRoutes('bottom'), ...buildSalesOrderRoutes('bottom'), // 其他底部模块 ], ), // More菜单路由组 GoRoute( path: '/more', builder: (context, state) => const MorePage(), routes: [ ...buildScheduleRoutes('more'), ...buildSalesOrderRoutes('more'), // 其他More模块 ], ), ], );
这样既保证了路由名称唯一,又完全复用了模块的页面和子路由结构。导航时直接使用生成的唯一路由名即可,比如context.pushNamed('bottom-schedule-detail', params: {'id': '123'})或context.pushNamed('more-schedule-detail', params: {'id': '123'})。
如果不想区分前缀,也可以直接用相对路由导航,但需要确保当前上下文的路由层级正确,不过前缀生成唯一路由名的方式更清晰,也更贴合GoRouter的声明式设计。
针对循环依赖路由的问题
不需要重复定义两个完全对称的路由,你可以通过以下两种方式解决:
1. 动态路由+通用页面组件
定义一个通用嵌套路由,通过路径参数区分当前模块类型和关联模块类型:
GoRoute( path: '/:moduleType/:moduleId/:relatedType/:relatedId', builder: (context, state) { final moduleType = state.pathParameters['moduleType']!; final moduleId = state.pathParameters['moduleId']!; final relatedType = state.pathParameters['relatedType']!; final relatedId = state.pathParameters['relatedId']!; // 根据类型渲染对应页面,复用组件逻辑 if (moduleType == 'case') { return CasePage(id: moduleId, relatedSalesOrderId: relatedId); } else if (moduleType == 'sales-order') { return SalesOrderPage(id: moduleId, relatedCaseId: relatedId); } // 兜底页面 return const NotFoundPage(); }, )
然后在页面中加入循环检测逻辑:比如在CasePage跳转到SalesOrder时,检查当前栈的上一个路由是否是对应的SalesOrder->Case,若是则阻止跳转,或替换当前路由而非push。
2. 用redirect阻止循环跳转
如果一定要保留两个对称路由结构,可以复用页面组件,同时通过GoRouter的redirect函数阻止无限循环:
final router = GoRouter( redirect: (context, state) { final currentPath = state.fullPath; final previousPath = GoRouterState.of(context).previousFullPath; // 检测循环跳转:case/:id/sales-order/:sid <-> sales-order/:sid/case/:id if (currentPath != null && previousPath != null) { final currentSegments = currentPath.split('/'); final previousSegments = previousPath.split('/'); if (currentSegments.length == 5 && previousSegments.length == 5) { final isLoop = currentSegments[1] == previousSegments[3] && currentSegments[2] == previousSegments[4] && currentSegments[3] == previousSegments[1] && currentSegments[4] == previousSegments[2]; if (isLoop) { // 阻止跳转,返回当前路径 return previousPath; } } } return null; }, routes: [ GoRoute( path: '/case/:id/sales-order/:sid', builder: (context, state) => SalesOrderPage( id: state.pathParameters['sid']!, relatedCaseId: state.pathParameters['id']!, ), ), GoRoute( path: '/sales-order/:sid/case/:id', builder: (context, state) => CasePage( id: state.pathParameters['id']!, relatedSalesOrderId: state.pathParameters['sid']!, ), ), ], );
这里两个路由复用了页面组件,仅路径参数顺序不同,同时通过redirect检测循环跳转,避免无限push。另外也可以写一个生成对称路由的函数,传入模块类型对自动生成路由,减少重复代码。
内容的提问来源于stack exchange,提问作者JoKr
相关产品推荐
相关产品推荐

