GoRouter StatefulShellRoute.indexedStack导航崩溃问题求助
问题解决:GoRouter StatefulShellRoute.indexedStack导航与状态传递问题
问题核心
- 无法直接导航到
VistaGeneralEmpresa:StatefulShellRoute本身不支持设置路由名称或路径,只能通过其内部分支的初始路由进入 - 切换分支崩溃:切换到
VistaCaja/VistaAjustes时,分支路由的state.extra为null,导致无法获取Empresa对象
解决方案
1. 给Shell路由添加入口GoRoute
通过在StatefulShellRoute外层包裹一个GoRoute,设置专属的名称和路径,实现直接导航到VistaGeneralEmpresa,同时将Empresa传递到Shell的根组件。
2. 共享Empresa对象到所有分支
不再依赖路由extra传递Empresa,而是在VistaGeneralEmpresa中通过InheritedWidget将Empresa共享给所有子分支页面,确保切换分支时每个页面都能获取到有效对象。
修改后的代码
1. 路由配置
GoRoute( name: 'vistaGeneralEmpresa', path: '/vistaGeneralEmpresa', builder: (context, state) { // 从入口路由的extra获取Empresa final Empresa empresa = state.extra as Empresa; return StatefulShellRoute.indexedStack( builder: (context, shellState, navigationShell) { return VistaGeneralEmpresa( empresa: empresa, navigationShell: navigationShell, ); }, branches: <StatefulShellBranch>[ StatefulShellBranch( navigatorKey: _rootNavigatorInicio, routes: <RouteBase>[ GoRoute( name: 'inicioEmpresa', path: 'inicioEmpresa', // 改为相对路径,依赖外层的/vistaGeneralEmpresa builder: (context, state) { // 从共享的Provider获取Empresa final empresa = EmpresaProvider.of(context).empresa; return VistaInicioEmpresa(empresa: empresa); }, routes: [ GoRoute( name: 'gestionInventario', path: 'gestionInventario', builder: (context, state) { final empresa = EmpresaProvider.of(context).empresa; return VistaGeneralInventario(empresa); }), GoRoute( name: 'historial', path: 'historial', builder: (context, state) { final empresa = EmpresaProvider.of(context).empresa; return VistaHistorial(empresa: empresa); }), GoRoute( name: 'estadisticas', path: 'estadisticas', builder: (context, state) { final empresa = EmpresaProvider.of(context).empresa; return VistaEstadisticas(empresa: empresa); }), ]), ), StatefulShellBranch( navigatorKey: _rootNavigatorCaja, routes: <RouteBase>[ GoRoute( path: 'caja', // 改为相对路径,与外层路由组合为/vistaGeneralEmpresa/caja builder: (context, state) { final empresa = EmpresaProvider.of(context).empresa; return VistaCaja(empresa: empresa); }, routes: []), ], ), StatefulShellBranch( navigatorKey: _rootNavigatorAjustes, routes: <RouteBase>[ GoRoute( path: 'ajustes', // 改为相对路径,与外层路由组合为/vistaGeneralEmpresa/ajustes builder: (context, state) { final empresa = EmpresaProvider.of(context).empresa; return VistaAjustesEmpresa(empresa: empresa); }, routes: [], ), ], ), ], ); }, ),
2. 实现Empresa共享的InheritedWidget
class EmpresaProvider extends InheritedWidget { final Empresa empresa; const EmpresaProvider({ super.key, required this.empresa, required super.child, }); // 提供便捷的获取方法 static EmpresaProvider of(BuildContext context) { final provider = context.dependOnInheritedWidgetOfExactType<EmpresaProvider>(); assert(provider != null, '未找到EmpresaProvider,请确保在父组件中包裹'); return provider!; } @override bool updateShouldNotify(EmpresaProvider oldWidget) { return empresa != oldWidget.empresa; } }
3. 修改VistaGeneralEmpresa,包裹Provider
class VistaGeneralEmpresa extends StatefulWidget { final Empresa empresa; final StatefulNavigationShell navigationShell; const VistaGeneralEmpresa({ super.key, required this.empresa, required this.navigationShell, }); @override State<VistaGeneralEmpresa> createState() => _VistaGeneralEmpresaState(); } class _VistaGeneralEmpresaState extends State<VistaGeneralEmpresa> { int selectedIndex = 0; void _goBranch(int index) { widget.navigationShell.goBranch( index, initialLocation: index == widget.navigationShell.currentIndex, ); } @override Widget build(BuildContext context) { // 用EmpresaProvider包裹整个页面,让所有子分支都能获取到Empresa return EmpresaProvider( empresa: widget.empresa, child: Scaffold( body: SizedBox( width: double.infinity, height: double.infinity, child: widget.navigationShell, ), bottomNavigationBar: GNav( onTabChange: (value) { setState(() { selectedIndex = value; _goBranch(selectedIndex); }); }, selectedIndex: selectedIndex, gap: 8, backgroundColor: Colors.amberAccent, color: const Color.fromARGB(255, 70, 70, 70), activeColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20), tabs: const [ GButton(icon: Icons.home, text: 'Inicio'), GButton(icon: Icons.inventory, text: 'Caja'), GButton(icon: Icons.settings, text: 'Ajustes') ], ), ), ); } }
4. 修改导航调用代码
void _navigateToVistaEmpresa(BuildContext context) { // 直接导航到Shell的入口路由 context.goNamed("vistaGeneralEmpresa", extra: empresa); }
关键说明
- 外层GoRoute作为Shell的入口,解决了无法直接导航到
VistaGeneralEmpresa的问题 - 使用InheritedWidget共享
Empresa,避免了切换分支时extra为null的崩溃问题,同时让所有子页面无需重复传递Empresa参数 - 分支路由路径改为相对路径后,既支持通过底部导航切换,也支持直接通过
/vistaGeneralEmpresa/caja这类路径访问对应分支
内容的提问来源于stack exchange,提问作者Dox
相关产品推荐
相关产品推荐

