Flutter中如何在StatefulNavigationShell外部通过GoRouter获取路由数据?
解决方案:通过Riverpod+自定义导航观察者同步路由状态到侧边栏
核心思路是用自定义NavigationObserver捕获所有路由变化,将关键路由信息(如路由名称、maiteId参数)同步到Riverpod的状态管理中,让侧边栏组件可以监听这个状态并更新UI。
步骤1:定义路由状态的StateNotifier
创建一个StateNotifier来保存当前路由的核心信息,避免直接传递整个GoRouterState(减少不必要的组件重建):
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; // 保存当前路由的关键信息 class CurrentRouteState { final String? routeName; final String? maiteId; CurrentRouteState({this.routeName, this.maiteId}); } // 管理路由状态的Notifier class CurrentRouteStateNotifier extends StateNotifier<CurrentRouteState> { CurrentRouteStateNotifier() : super(CurrentRouteState()); // 更新路由状态 void update(GoRouterState state) { state = CurrentRouteState( routeName: state.name, maiteId: state.pathParameters['maiteId'], ); } } // 全局可用的Provider final currentRouteStateProvider = StateNotifierProvider<CurrentRouteStateNotifier, CurrentRouteState>((ref) { return CurrentRouteStateNotifier(); });
步骤2:自定义NavigationObserver捕获路由变化
创建导航观察者,在路由发生push/pop/replace时同步路由状态到Riverpod:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; class RouteStateObserver extends NavigatorObserver { final WidgetRef ref; RouteStateObserver(this.ref); @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPush(route, previousRoute); _syncRouteState(route); } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPop(route, previousRoute); _syncRouteState(previousRoute); } @override void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) { super.didReplace(newRoute: newRoute, oldRoute: oldRoute); _syncRouteState(newRoute); } // 同步路由状态到Riverpod void _syncRouteState(Route<dynamic>? route) { if (route is PageRoute && route.settings is GoRouteMatch) { final goRouteMatch = route.settings as GoRouteMatch; ref.read(currentRouteStateProvider.notifier).update(goRouteMatch.state); } } }
步骤3:更新GoRouter配置,添加自定义观察者
修改routerProvider,将自定义观察者添加到根路由和StatefulShellBranch中(确保分支内的子路由变化也能被捕获):
final routerProvider = Provider<GoRouter>((ref) { final routeObserver = RouteStateObserver(ref); return GoRouter( navigatorKey: rootNavigatorKey, initialLocation: '/team', // 根导航添加观察者 observers: [routeObserver], routes: <RouteBase>[ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return MainWindow(navigationShell: navigationShell); }, branches: [ StatefulShellBranch( // 分支内添加观察者,捕获子路由变化 observers: [routeObserver], navigatorKey: teamModeNavigatorKey, routes: <RouteBase>[ GoRoute( path: '/team', name: 'teamHome', builder: (context, state) => const TeamHomePage(), ), GoRoute( name: "marketplace", path: '/team/marketplace', builder: (context, state) => const MarketplacePage(), ), GoRoute( name: "maiteDashboard", path: '/team/:maiteId', builder: (context, state) { final maiteId = state.pathParameters['maiteId']; return ChatScreen(maiteId: maiteId); }, ), ], ), StatefulShellBranch( observers: [routeObserver], routes: <RouteBase>[ GoRoute( path: '/projects', name: 'projectRoute', builder: (context, state) => const ProjectPage(), ), ], ), ], ), ], ); });
步骤4:侧边栏卡片监听路由状态,更新样式
在myMaiteCard组件中监听路由状态,根据匹配结果设置卡片激活样式:
Widget myMaiteCard(WidgetRef ref, BuildContext context, String maiteId) { // 监听当前路由状态 final currentRoute = ref.watch(currentRouteStateProvider); // 判断卡片是否处于激活状态 final isActive = currentRoute.routeName == "maiteDashboard" && currentRoute.maiteId == maiteId; return ClickableWidget( borderRadius: BorderRadius.circular(6), hoverColor: Colors.blueGrey.withOpacity(0.075), // 激活状态下的背景色 backgroundColor: isActive ? Colors.blue.withOpacity(0.1) : Colors.transparent, onPressed: () { GoRouter.of(context).pushNamed( "maiteDashboard", pathParameters: {"maiteId": maiteId}, ); }, child: // 你的卡片内容组件 ); }
关键优势说明
- 规避Riverpod构建时修改状态的错误:所有状态更新都在导航回调中执行,不属于组件构建流程。
- 全面覆盖路由场景:包括根路由切换、分支内子路由跳转、返回操作等。
- 轻量化状态同步:仅保存必要的路由信息,减少不必要的组件重建。
内容的提问来源于stack exchange,提问作者Julien Elkaim
相关产品推荐
相关产品推荐

