Flutter中ModalBottomSheetRoute与GoRouter底部弹窗内导航失效问题
问题:Flutter中使用go_router在ModalBottomSheet内实现嵌套导航失败
我在Flutter中尝试用ModalBottomSheetRoute和go_router实现底部弹窗内的导航功能,但无法在弹窗内跳转不同路径。想配置一个带通用ModalBottomSheetRoute的ShellRoute,让嵌套的GoRoute在弹窗内完成导航,目前无报错但功能不生效。
问题演示:点击test1页面的按钮无法跳转到test2页面,弹窗内容无变化。
原代码
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; void main() { runApp(const MyApp()); } final GlobalKey<NavigatorState> settingsNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'settings'); final GoRouter _router = GoRouter( initialLocation: '/', routes: <RouteBase>[ GoRoute( path: '/', builder: (context, state) => const MyHomePage(), ), ShellRoute( navigatorKey: settingsNavigatorKey, pageBuilder: (context, state, widget) { return BottomSheetPage( key: state.pageKey, showDragHandle: true, child: widget, ); }, // builder: (context, state, widget) { // return Scaffold(appBar: AppBar(), body: Center(child: widget)); // }, routes: [ GoRoute( parentNavigatorKey: settingsNavigatorKey, path: '/test1', builder: (context, state) => Scaffold( appBar: AppBar(title: Text('This is test1 Page'),), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ TextButton( onPressed: () { GoRouter.of(context).push('/test1/test2'); }, child: Text('go to test2')), Text('test1'), ], ), ), routes: [ GoRoute( parentNavigatorKey: settingsNavigatorKey, path: 'test2', builder: (context, state) => Scaffold( appBar: AppBar(), body: Center(child: Text('test2')), ), ), ], ), ], ), ], ); class BottomSheetPage<T> extends Page<T> { final Widget child; final bool showDragHandle; final bool useSafeArea; const BottomSheetPage({ required this.child, this.showDragHandle = false, this.useSafeArea = true, super.key, }); @override Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>( settings: this, isScrollControlled: true, showDragHandle: showDragHandle, useSafeArea: useSafeArea, backgroundColor: Colors.transparent, builder: (context) => child, ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Nested Navigation BottomSheet'), ), body: Center( child: TextButton( child: Text("Push the test1"), onPressed: () { GoRouter.of(context).push('/test1'); }, )), ); } }
解决方案
问题原因
ShellRoute的pageBuilder返回的BottomSheetPage没有为嵌套导航提供独立的Navigator容器,导致子路由的导航操作仍使用根Navigator,而非弹窗内的嵌套Navigator,所以弹窗内容不会更新。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; void main() { runApp(const MyApp()); } final GlobalKey<NavigatorState> settingsNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'settings'); final GoRouter _router = GoRouter( initialLocation: '/', routes: <RouteBase>[ GoRoute( path: '/', builder: (context, state) => const MyHomePage(), ), ShellRoute( navigatorKey: settingsNavigatorKey, pageBuilder: (context, state, widget) { return BottomSheetPage( key: state.pageKey, showDragHandle: true, child: widget, ); }, routes: [ GoRoute( path: '/test1', builder: (context, state) => Scaffold( appBar: AppBar(title: const Text('This is test1 Page')), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ TextButton( onPressed: () { GoRouter.of(context).push('/test1/test2'); }, child: const Text('go to test2'), ), const Text('test1'), ], ), ), routes: [ GoRoute( path: 'test2', builder: (context, state) => const Scaffold( appBar: AppBar(), body: Center(child: Text('test2')), ), ), ], ), ], ), ], ); class BottomSheetPage<T> extends Page<T> { final Widget child; final bool showDragHandle; final bool useSafeArea; const BottomSheetPage({ required this.child, this.showDragHandle = false, this.useSafeArea = true, super.key, }); @override Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>( settings: this, isScrollControlled: true, showDragHandle: showDragHandle, useSafeArea: useSafeArea, backgroundColor: Colors.transparent, builder: (context) => Navigator( key: settingsNavigatorKey, onGenerateRoute: (settings) => MaterialPageRoute( builder: (_) => child, settings: settings, ), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Nested Navigation BottomSheet'), ), body: Center( child: TextButton( child: const Text("Push the test1"), onPressed: () { GoRouter.of(context).push('/test1'); }, ), ), ); } }
关键修改点
- 为ModalBottomSheet添加嵌套Navigator:在
BottomSheetPage的createRoute方法中,将原来直接返回child改为返回一个带有settingsNavigatorKey的Navigator,确保子路由的导航操作绑定到弹窗内的Navigator。 - 移除子GoRoute的parentNavigatorKey:ShellRoute已经指定了
navigatorKey,子路由默认会使用该Shell的Navigator,无需额外设置parentNavigatorKey。
内容的提问来源于stack exchange,提问作者antbear
相关产品推荐
相关产品推荐

