使用GoRouter 5.0的ShellRoute切换标签页时如何返回嵌套路由?
问题:GoRouter 5.0结合ShellRoute实现嵌套导航时标签页状态丢失
我正尝试在GoRouter 5.0中结合ShellRoute实现嵌套导航。点击商品时可正确导航至嵌套的商品详情页,但切换到购物车标签页再切回商品标签页时,会回到根级的商品列表页,而非之前停留的商品详情页:

路由配置
enum AppRoute { products, product, cart, account, } final goRouter = GoRouter( initialLocation: '/products', navigatorKey: _rootNavigatorKey, debugLogDiagnostics: true, routes: [ ShellRoute( navigatorKey: _shellNavigatorKey, builder: (context, state, child) { return ScaffoldWithBottomNavBar(child: child); }, routes: [ // 商品列表路由 GoRoute( path: '/products', name: AppRoute.products.name, redirect: (context, state) { print(state.toString()); return null; }, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, restorationId: state.pageKey.value, child: const ProductsListScreen(), ), routes: [ // 商品详情嵌套路由 GoRoute( path: ':id', name: AppRoute.product.name, pageBuilder: (context, state) { final productId = state.params['id']!; // TODO: 实现Cupertino侧滑过渡 return MaterialPage( key: state.pageKey, restorationId: state.pageKey.value, child: ProductScreen(productId: productId), ); }, ), ], ), // 购物车路由 GoRoute( path: '/cart', name: AppRoute.cart.name, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const ShoppingCartScreen(), ), ), // 账户页面路由 GoRoute( path: '/account', name: AppRoute.account.name, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const AccountScreen(), ), ), ], ), ], );
ScaffoldWithBottomNavBar组件的build方法
@override Widget build(BuildContext context) { return Scaffold( body: widget.child, bottomNavigationBar: BottomNavigationBar( type: BottomNavigationBarType.fixed, unselectedItemColor: Colors.grey, selectedItemColor: Colors.black87, currentIndex: _selectedIndex, items: [ BottomNavigationBarItem( icon: const Icon( Icons.list, ), label: '商品'.hardcoded, ), BottomNavigationBarItem( icon: const ShoppingCartIcon(), label: '购物车'.hardcoded, ), BottomNavigationBarItem( icon: const Icon( Icons.account_circle, ), label: '账户'.hardcoded, ), ], onTap: (index) => _tap(context, index), ), ); }
标签按钮的回调逻辑
void _tap(BuildContext context, int index) { setState(() => _selectedIndex = index); // 用于更新标签高亮状态 // 根据索引导航到对应路由 if (index == 0) { context.goNamed(AppRoute.products.name); } else if (index == 1) { context.goNamed(AppRoute.cart.name); } else if (index == 2) { context.goNamed(AppRoute.account.name); } }
问题分析与尝试方案
我清楚当前实现无效的原因:直接让GoRouter导航到ShellRoute内的各个根路由。理想状态是切换标签页时,GoRouter能记住该标签页之前处于嵌套路由还是根路由状态。注意:和TabBar不同,BottomNavigationBar没有可用于切换索引的控制器。GoRouter 5.0版本较新,相关常见场景的文档和示例较少。
我曾尝试将选中的商品ID存储为应用状态,在/products路由的redirect函数中使用,但状态重置逻辑复杂,且NavigationObserver的尝试未达到预期效果。
更新说明
2023年6月28日更新
GoRouter 7.1.0版本新增了StatefulShellRoute类,专门支持这类标签页状态保留的场景。我撰写了完整教程讲解其用法:
- Flutter底部导航栏结合GoRouter实现有状态嵌套路由
内容的提问来源于stack exchange,提问作者bizz84
相关产品推荐
相关产品推荐

