Flutter嵌套Navigator用GlobalKey跨兄弟组件导航报重复Key错误求替代方案
错误原因
你报错的核心原因是 RightSideNavigator 里的 navigationKey 是静态全局唯一的,但你在 PageOne 和 PageTwo 中都实例化了 RightSideNavigator 组件。当你从 PageOne 跳转到 PageTwo 时,PageOne 还保存在路由栈中没有被销毁,此时 widget 树中同时存在两个持有同一个 GlobalKey 的 Navigator 组件,就触发了重复 GlobalKey 的报错。
可行替代方案
方案1:调整布局结构,全局唯一侧边导航(最推荐)
不要在每个业务页面(PageOne/PageTwo)中单独嵌入侧边导航,把侧边导航提到根层级,和主内容区的路由平级,全局只保留一个 RightSideNavigator 实例,从根源上避免重复 GlobalKey 的问题,也符合你全局控制侧边路由的需求。
修改后的 MyApp 代码示例:
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Right Slider Navigation Demo', theme: ThemeData( primarySwatch: Colors.blueGrey, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: Scaffold( body: Row( children: [ // 左侧主内容区,单独用一个Navigator托管业务页面路由 Expanded( child: Navigator( initialRoute: "/page1", onGenerateRoute: (settings) { Widget page; switch(settings.name) { case "/page1": page = const PageOne(); break; case "/page2": page = const PageTwo(); break; default: page = const PageOne(); } return MaterialPageRoute(builder: (_) => page); }, ), ), // 右侧全局唯一的侧边导航,不会随主内容路由切换重建 Container( decoration: BoxDecoration(border: Border.all(color: Colors.blueGrey)), child: ConstrainedBox( constraints: BoxConstraints.tightFor(width: MediaQuery.of(context).size.width * 0.3), child: RightSideNavigator( initialRoute: "/", routes: { '/': (context) => const RightSideNavigationView(), '/menu_one': (context) => const MenuOne(), }, ), ), ) ], ), ), ); } }
对应修改业务页面的跳转逻辑:PageOne/PageTwo 去掉内嵌的侧边导航代码,跳转到PageTwo改用 Navigator.of(context).pushNamed("/page2") 即可,原有的 RightSideNavigator.navigator().push 逻辑完全不用改。
方案2:用事件通信替代全局GlobalKey
如果你必须保留每个页面单独嵌入侧边导航的结构,可以用状态管理的事件分发机制来触发路由跳转,完全不需要全局Key:
- 定义一个全局的事件控制器,比如用Flutter自带的
StreamController:
// 全局事件单例 final sideNavEvent = StreamController<RouteSettings>.broadcast();
- 把
RightSideNavigator改为StatefulWidget,在初始化时监听事件,收到事件后调用当前组件内的Navigator.of(context)执行跳转:
@override void initState() { super.initState(); _subscription = sideNavEvent.stream.listen((routeSettings) { if (mounted) { Navigator.of(context).pushNamed(routeSettings.name, arguments: routeSettings.arguments); } }); } @override void dispose() { _subscription.cancel(); super.dispose(); }
- 所有需要触发侧边导航跳转的位置,改为发送事件即可:
// 原来的RightSideNavigator.navigator().pushNamed("/menu_one") 改为 sideNavEvent.add(const RouteSettings(name: "/menu_one"));
内容的提问来源于stack exchange,提问作者Sanket
相关产品推荐
相关产品推荐

