You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:40:59