Flutter中如何使用GoRouter访问StatefulNavigationShell之外的路由数据?

问题描述 投票:0回答:1

首先,非常感谢您帮助我完成这个挑战!

我努力实现的目标

我的 Flutter Desktop 应用程序分为导航侧栏和内容区域。路线仅关注内容区域(StatefulNavigationShell)。

但是,在某些情况下,我希望侧边栏的某些小部件能够访问路线(并非所有路线)的路径参数。但我连续三天都失败了...我错过了一些东西!

一些背景

我使用 Riverpod 进行状态管理,使用 GoRouter 进行路由。

我的应用程序有两种主要“模式”:

  • “项目”,我可以在其中与我的人工智能代理(“maites”)一起完成特定任务。
  • “团队”,我在其中管理我的人工智能代理(“maites”)并可以与他们每个人交谈。

当我在每种模式之间切换时,我想进入之前处于该模式的页面。因此,我使用了2个StatefulShellBranch来代表这2种模式,因此它们有自己的导航状态。

这是我的应用程序的样子:

My Desktop App with sections highlighted

  • 红色 + 绿色 -> 我的侧边栏小部件。
  • 红色 -> 让我在分支之间切换的按钮。
  • 绿色 -> 导航区域。每个分支都有不同的导航区域,但是当我在同一分支/模式内的页面之间切换时,它是相同的导航区域小部件。
  • 紫色 -> 内容区域,与路线相关的区域。

但是,当我在Team分支中时,如果我单击maite的卡片(绿色部分),我会打开路线“/team/maites/:maiteId”,这是与该AI代理的聊天。 **我努力实现的目标是,当路径参数“maiteId”与该卡具有的 maiteId 匹配时,绿色区域中的 maite 卡看起来不同(即按下按钮)。

但这就是问题所在!导航侧边栏位于 NavigationShell 之外,因此它看不到我们所在的路线,并且不会根据新路线重建!!

请注意:在团队模式下,我可以有其他与特定 maite 无关的路由(因此,没有路径参数“maiteId”),即 teamDashboard 路由、maiteMarketplace 路由等。

问题

我尝试了很多技术来从 Shell 范围“泄漏”路由信息:

  • 使用我的状态管理解决方案 Riverpod / 提供商。如果我在路由的构建器方法中更改提供程序状态,Riverpod 会抛出错误,指出我无法在小部件构造中修改提供程序的状态!我尝试将 GoRouter 之类的元素封装在 StateProvider 中,这有效,但不知怎的,我无法用这个对象真正访问当前路由的名称和路径参数。
  • 使用 RouteObserver 或 NavigationObserver,但它们的行为非常奇怪!它并不总是看到路线的名称,而且 didPush / didPop 等奇怪的是并不总是触发,即使我总是使用相同的方法在路线之间移动(GoRouter#pushNamed),所以行为应该是相同的。

...我只是想从侧边栏获得当前路由的设置数据,但用 GoRouter 似乎很难实现。

代码片段

一些代码片段(如果您觉得有助于理解上下文)->

路由器的外观:

final routerProvider = Provider<GoRouter>((ref) {
  return GoRouter(
    redirect: (context, state) {
      // An attempt to leak the GoRouterState
      WidgetsBinding.instance.addPostFrameCallback((_) {
        ref.read(goRouterStateProvider.notifier).state = state;
      });
      return null;
    },
    observers: [],
    navigatorKey: rootNavigatorKey,
    initialLocation: '/team',
    routes: <RouteBase>[
      StatefulShellRoute.indexedStack(
        builder: (BuildContext context, GoRouterState state,
            StatefulNavigationShell navigationShell) {
          return MainWindow(navigationShell: navigationShell);
        },
        branches: [
          TeamBranch(
            observers: [RouteChangeObserver(null)], // Another attempt to leak the information
            navigatorKey: teamModeNavigatorKey,
            routes: <RouteBase>[
              MyRoutes.teamHome,
              GoRoute(
                name: "marketplace",
                path: '/marketplace',
                builder: (BuildContext context, GoRouterState state) {
                  return MarketplacePage();
                },
              ),
              GoRoute(
                name: "maiteDashboard",
                path: '/team/:maiteId',
                builder: (BuildContext context, GoRouterState state) {
                  String? maiteId = state.pathParameters['maiteId'];
                  return ChatScreen(maiteId == null ? null : ChatKey.quickTalk(maiteId!!));
                },
              ),
            ],
          ),
          ProjectBranch(
            routes: <RouteBase>[
              MyRoutes.projectRoute,
            ],
          ),
        ],
      ),
    ],
  );
});

我如何在路线之间切换的 2 个示例,
前往市场 ->

//...  ClickableWidget(
          hoverColor: Colors.transparent,
          padding: const EdgeInsets.all(8),
          onPressed: () {
              GoRouter.of(context).pushNamed("marketplace");
          },
          child: //...

到 maite 对话 (/team/:maiteId) ->

  Widget myMaiteCard(WidgetRef ref, BuildContext context) {
    return ClickableWidget(
      borderRadius: BorderRadius.circular(6),
      hoverColor: Colors.blueGrey.withOpacity(0.075),
      onPressed: () {
        GoRouter.of(context).pushNamed("maiteDashboard",
            pathParameters: {"maiteId": maite.maiteId});
      },
      child: //..
flutter riverpod flutter-go-router
1个回答
0
投票

我以前也遇到过类似的问题,例如尝试在复杂的应用程序结构中的不同元素之间同步状态。但有一些可能有效的技术想法:

  • 采用全局状态管理解决方案:而不是塞满
    将信息直接传递到参数中,您可以制造一个
    全局状态(使用 Riverpod)保存路线信息。每当这个
    更改,使用您的路线构建器或自定义 RouteInformationParser
    更新该状态。然后您的侧边栏小部件会监听全局 状态。

    final currentRouteProvider = StateProvider<String>((ref) => '');
    
    // In your route builder:
    builder: (context, state) {
      ref.read(currentRouteProvider.notifier).state = state.location;
      return YourWidget();
    }
    
    // In your sidebar:
    final currentRoute = ref.watch(currentRouteProvider);
    // Use currentRoute to determine UI state
    
  • 使用 InheritedWidget:。 制作您自己的新组件,包装您的整个应用程序,并为其提供当前的路线信息。每当路线发生变化时,更新此小部件;然后你的侧边栏可以通过 MyRouteInheritedWidget.of(context) 获取此信息。

  • 自定义GoRouter:子类GoRouter并添加路由更改的回调;然后在您的应用程序中使用这个自定义路由器,并传递回调来更新侧边栏的状态。

  • 使用GoRouter的refreshListenable:创建一个自定义的Listenable,每当路由发生变化时都会更新,并将其与GoRouter的refreshListenable参数一起使用。这会强制整个应用程序在路线更改时重新构建,并使您的侧边栏更新能够成功。

  • 重新思考你的小部件树: 如果可能,请考虑重组您的应用程序,使侧边栏成为每条路线的一部分,而不是位于导航外壳之外。这将允许它随着路线的改变而自然地重建。

© www.soinside.com 2019 - 2024. All rights reserved.