在GoRouter中使用ShellRoute:让/b路由直接全屏显示的问题
我正在调试GoRouter仓库里的shellroute示例,标签页B的路由代码如下:
GoRoute( path: '/b', builder: (BuildContext context, GoRouterState state) { return const ScreenB(); }, routes: <RouteBase>[ /// 和"/a/details"类似,但通过指定[parentNavigatorKey]在根Navigator显示,会覆盖Screen B和应用shell GoRoute( path: 'details', parentNavigatorKey: _rootNavigatorKey, builder: (BuildContext context, GoRouterState state) { return const DetailsScreen(label: 'B'); }, ), ], ),
我的需求是:点击标签页“B”时,不在B的嵌套窗口显示内容,而是直接实现和/b/details完全一样的效果——以全屏形式堆叠显示新页面。
我尝试给/b路由添加parentNavigatorKey: _rootNavigatorKey,但程序还没运行就报错:
Error: Assertion failed: route.dart:408 route.parentNavigatorKey == null || route.parentNavigatorKey == navigatorKey "sub-route's parent navigator key must either be null or has the same navigator key as parent's key"
疑问点:
- 为什么不能让
/b路由直接全屏入栈,而非要依赖/b/details?B的标签窗口没有实际内容需要展示。 - 这个断言错误的含义是什么?
- 这是无法实现需求的Bug吗?
解答:
断言错误的含义
这个断言是GoRouter的路由规则校验:子路由的parentNavigatorKey要么为null,要么必须和父路由使用的Navigator Key完全一致。在shellroute结构里,/b作为shell的子路由,默认绑定shell对应的嵌套Navigator Key,你给它指定根Navigator的_rootNavigatorKey,和父路由的Key不一致,触发了校验失败。
为什么不能直接给/b加parentNavigatorKey
GoRouter的shell路由结构中,每个标签页路由(/a、/b)都是shell路由的子路由,默认归属shell对应的嵌套Navigator。如果强行给这类直接挂载在shell下的路由指定根Navigator Key,就违反了GoRouter的父子路由Navigator Key一致性规则——父路由(shell)用的是自身的Navigator Key,子路由不能跨级使用根的Key。
实现需求的正确方式
你的需求是点击B标签直接全屏打开DetailsScreen,不需要中间的ScreenB,完全可以通过调整路由逻辑实现:
利用GoRouter的redirect功能,让/b路由自动跳转到/b/details,既符合路由规则又能达到效果:
GoRoute( path: '/b', redirect: (context, state) => '/b/details', routes: <RouteBase>[ GoRoute( path: 'details', parentNavigatorKey: _rootNavigatorKey, builder: (BuildContext context, GoRouterState state) { return const DetailsScreen(label: 'B'); }, ), ], ),
点击B标签时,会自动重定向到/b/details,实现全屏堆叠的效果。
这不是Bug
这不是GoRouter的Bug,而是它的设计规则:shell路由下的子路由默认属于嵌套Navigator,不能直接跨到根Navigator,目的是维护路由结构的层级一致性,避免Navigator管理混乱。通过重定向的方式完全可以实现你的需求,无需破坏现有规则。
内容的提问来源于stack exchange,提问作者Randal Schwartz

