如何根据屏幕尺寸使用 go_router 有条件地嵌套路由?

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

我正在开发一个使用 go_router 进行导航的 Flutter 应用程序,我遇到了一个很常见的响应式设计场景,但我找不到一种直接的方法来处理路由。

简化场景

  • 该应用程序具有三个顶级路线:
    /home
    /posts
    /profile
  • 每条路线都可以通过顶级导航访问:紧凑屏幕上的底部导航栏,中型屏幕上的导航栏,以及扩展屏幕上的持久导航抽屉
  • 在顶级路线之间进行动画转换时,导航 UI 保持不变
现在,假设在

/posts

 中,当用户选择带有 ID 的特定帖子时,他们会导航到 
/posts/ID
 处的帖子详细信息。

我正在寻找的行为

    在紧凑的屏幕上,帖子详细信息视图占据整个屏幕,过渡到底部导航栏的顶部,将其隐藏,直到用户导航回来
  • 在较大的屏幕上,应保留持久导航侧边栏/导轨,仅更新屏幕的内部内容以显示帖子详细信息
  • 理想的解决方案是:
    1. 使用 go_router 的
    2. 类型安全路线
    3. 调整屏幕大小时在每种布局类型之间无缝转换
我尝试过的事情

我正在使用

ShellRoute

 来显示持久导航。我知道我可以指定路线的 
parentNavigatorKey
 以使用根导航器而不是 shell,但我不确定如何根据当前屏幕尺寸动态更改此设置。

有没有办法根据屏幕大小有条件地在 go_router 中嵌套或顶级路由?

flutter responsive-design flutter-go-router
2个回答
0
投票
需要考虑的一些事项:

    我有一个类似的问题,但我没有根据你的答案进行调整,所以有一些事情需要改变
  • 这可能不是解决问题最有效的方法
  • 我也没有使用类型安全路由,但您应该能够轻松地进行所需的更改
    我创建了一个 GoRouter,其中我将不同的设备宽度分成不同的路由
  final GoRouter goRouter = GoRouter(       initialLocation: "/mobile",       navigatorKey: _rootNavigatorKey,       redirect: (context, state) {         MediaQueryData mediaQuery = MediaQuery.of(context);         return navigatorService.navigate(state, mediaQuery);       },       routes: [         ShellRoute(             navigatorKey: _shellNavigatorKey,             builder: (BuildContext context, GoRouterState state, Widget child) {               return ChatNavigator(child: child);             },             routes: [               GoRoute(                   parentNavigatorKey: _shellNavigatorKey,                   path: '/desktop',                   builder: (BuildContext context, GoRouterState state) {                     return const Nothing();                   },                   routes: [                     GoRoute(                       parentNavigatorKey: _shellNavigatorKey,                       path: 'chat/:id',                       builder: (BuildContext context, GoRouterState state) {                        final id = state.pathParameters['id'];                         return Chat(                           id: id == "null" ? null : int.parse(id!),                         );                       },                     ),                     GoRoute(                       parentNavigatorKey: _shellNavigatorKey,                       path: 'settings',                       builder: (BuildContext context, GoRouterState state) {                         return const Settings();                       },                     ),                   ]),             ]),         GoRoute(             path: "/mobile",             parentNavigatorKey: _rootNavigatorKey,             builder: (BuildContext context, GoRouterState state) {               return const ChatNavigator(                 child: SizedBox.shrink(),               );             },             routes: [               GoRoute(                 path: 'chat/:id',                 builder: (BuildContext context, GoRouterState state) {                   final id = state.pathParameters['id'];                   return Chat(                     id: id == "null" ? null : int.parse(id!),                   );                 },               ),               GoRoute(                 path: 'settings',                 builder: (BuildContext context, GoRouterState state) {                   return const Settings();                 },               ),             ]),       ]);
这里的问题还在于您有重复的路线,为了解决这个问题,您可以将它们放入列表中并将其附加到不同的路线。
2.你还可以看到我使用了GoRouter的redirect方法,在那里我会拦截每个push或replace请求。因为我创建了具有相同内容的多条路线,所以我需要以某种方式翻译这些一般请求,即:

GoRouter.of(context).push('/chat/${widget.chat.id}');

3. 为此,我创建了一个处理这些请求的类:

import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; class NavigatorService {   String? lastRoute;   String navigate(GoRouterState state, MediaQueryData mediaQuery) {     lastRoute = state.uri.toString().contains("/desktop") ||             state.uri.toString().contains("/mobile")         ? lastRoute         : state.uri.toString();     String path = state.uri.toString().replaceAll("/desktop", '').trim();     String changed = path.replaceAll("/mobile", "").trim();     if (mediaQuery.size.width >= 700.0) {       // Desktop Route       if (state.uri.toString().contains("/mobile")) {         return "/desktop$lastRoute";       }       return "/desktop$changed";     } else {       //Mobile       if (state.uri.toString().contains("/desktop")) {         return "/mobile$lastRoute";       }       return "/mobile$changed";     }   } }

    该类在GoRouter之前实例化
  static NavigatorService navigatorService = NavigatorService();   final GoRouter goRouter = GoRouter(   ....
我希望这个想法对你有帮助!

我还找到了一个由 Google 制作的软件包,它应该可以满足您的需求:

https://pub.dev/packages/adaptive_navigation


0
投票
尝试在代码中使用

navigationShell

。在选项卡/菜单导航方面它非常强大。这应该很容易集成到您的代码中,因为我看到它已经拥有所有必要的组件。

    使用
  • StatefulShellRoute.indexedStack
     定义根级路由。
  • 它有一个
  • builder
     变量,它给你 
    builder
  • 使用它,您可以在屏幕之间切换,获取当前索引,并使用这个单个变量在所有 3 种屏幕尺寸中渲染主体。
此方法维护每个父屏幕的层次结构和状态。

StatefulShellRoute.indexedStack( branches: [ StatefulShellBranch( routes: [ GoRoute( path: '/${Home.path}', builder: (_, __) => const Home() ), GoRoute( path: CounterView.path, builder: (_, __) => const CounterView(title: ''), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/${Profile.path}', builder: (_, __) => const Profile(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/${Settings.path}', builder: (_, __) => const Settings(), ), ], ), ], builder:(_, __, navigationShell) => ResponsiveNavigationBar( navigationShell: navigationShell, barButtons: [ BarItem(icon: const Icon(Icons.home), label: 'Home'), BarItem(icon: const Icon(Icons.person), label: 'Profile'), BarItem(icon: const Icon(Icons.settings), label: 'Settings') ], ), // ResponsiveNavigationBar ),

ResponsiveNavigationBar

 将像这样使用 
navigationShell

@override Widget build(BuildContext context) { if (MediaQuery.of(context).size.width < 500) { // Mobile phones return Scaffold( body: widget.navigationShell, bottomNavigationBar: BottomNavigationBar( items: widget.phoneBarButtons, onTap: (int index) => widget.navigationShell.goBranch(index), currentIndex: widget.navigationShell.currentIndex, ), ); } else if (MediaQuery.of(context).size.width > 1024) { _tabController.index = widget.navigationShell.currentIndex; // Web Browser / Desktop return Scaffold( body: Column( children: [ TabBar( tabs: widget.webBarButtons, onTap: (int index) => widget.navigationShell.goBranch(index), controller: _tabController, tabAlignment: TabAlignment.center, ), Expanded(child: widget.navigationShell), ], ), ); } else { // iPads / Tablets return Scaffold( body: Row( children: [ NavigationRail( destinations: widget.tabletBarButtons, onDestinationSelected: (int index) => widget.navigationShell.goBranch(index), labelType: NavigationRailLabelType.all, selectedIndex: widget.navigationShell.currentIndex, ), Expanded(child: widget.navigationShell), ], ), ); } }
    
© www.soinside.com 2019 - 2024. All rights reserved.