我正在开发一个使用 go_router 进行导航的 Flutter 应用程序,我遇到了一个很常见的响应式设计场景,但我找不到一种直接的方法来处理路由。
/home
、/posts
和/profile
/posts
中,当用户选择带有 ID 的特定帖子时,他们会导航到
/posts/ID
处的帖子详细信息。我正在寻找的行为
ShellRoute
来显示持久导航。我知道我可以指定路线的
parentNavigatorKey
以使用根导航器而不是 shell,但我不确定如何根据当前屏幕尺寸动态更改此设置。有没有办法根据屏幕大小有条件地在 go_router 中嵌套或顶级路由?
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";
}
}
}
static NavigatorService navigatorService = NavigatorService();
final GoRouter goRouter = GoRouter(
....
我希望这个想法对你有帮助!我还找到了一个由 Google 制作的软件包,它应该可以满足您的需求:
navigationShell
。在选项卡/菜单导航方面它非常强大。这应该很容易集成到您的代码中,因为我看到它已经拥有所有必要的组件。
StatefulShellRoute.indexedStack
定义根级路由。
builder
变量,它给你
builder
。
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),
],
),
);
}
}