首先,非常感谢您帮助我完成这个挑战!
我的 Flutter Desktop 应用程序分为导航侧栏和内容区域。路线仅关注内容区域(StatefulNavigationShell)。
但是,在某些情况下,我希望侧边栏的某些小部件能够访问路线(并非所有路线)的路径参数。但我连续三天都失败了...我错过了一些东西!
我使用 Riverpod 进行状态管理,使用 GoRouter 进行路由。
我的应用程序有两种主要“模式”:
当我在每种模式之间切换时,我想进入之前处于该模式的页面。因此,我使用了2个StatefulShellBranch来代表这2种模式,因此它们有自己的导航状态。
这是我的应用程序的样子:
但是,当我在Team分支中时,如果我单击maite的卡片(绿色部分),我会打开路线“/team/maites/:maiteId”,这是与该AI代理的聊天。 **我努力实现的目标是,当路径参数“maiteId”与该卡具有的 maiteId 匹配时,绿色区域中的 maite 卡看起来不同(即按下按钮)。
但这就是问题所在!导航侧边栏位于 NavigationShell 之外,因此它看不到我们所在的路线,并且不会根据新路线重建!!
请注意:在团队模式下,我可以有其他与特定 maite 无关的路由(因此,没有路径参数“maiteId”),即 teamDashboard 路由、maiteMarketplace 路由等。
我尝试了很多技术来从 Shell 范围“泄漏”路由信息:
...我只是想从侧边栏获得当前路由的设置数据,但用 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: //..
我以前也遇到过类似的问题,例如尝试在复杂的应用程序结构中的不同元素之间同步状态。但有一些可能有效的技术想法:
采用全局状态管理解决方案:而不是塞满
将信息直接传递到参数中,您可以制造一个
全局状态(使用 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参数一起使用。这会强制整个应用程序在路线更改时重新构建,并使您的侧边栏更新能够成功。
重新思考你的小部件树: 如果可能,请考虑重组您的应用程序,使侧边栏成为每条路线的一部分,而不是位于导航外壳之外。这将允许它随着路线的改变而自然地重建。