我正在尝试在 flutter web 中实现持久菜单(侧边栏)。只要应用程序中的所有页面都直接列为菜单项,我就可以实现。 问题是我无法在“内容区域”中打开嵌套页面(页面未在侧面菜单中列出,但在某个按钮上单击从另一个页面内打开)。
我尝试了很多东西,从 Navigation Rail 到 GoRouter,再到侧边栏的不同包。
我不知道要在这里发布什么代码。
我的项目也用了Getx
无论我拥有什么代码,我都可以在内容区域中打开第一级页面,但嵌套页面作为新页面加载并且侧边栏丢失。
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
///
final MyMenuController menuController = Get.put(MyMenuController());
///
return Scaffold(
appBar: AppBar(
title: const Text('Side Menu Example'),
),
body: Row(
children: [
Expanded(
child: Container(
color: Colors.blue,
child: ListView(
children: [
ListTile(
title: const Text('Page 1'),
selected: menuController.selectedIndex.value == 0,
onTap: () {
menuController.selectTab(0);
},
),
ListTile(
title: const Text('Page 2'),
selected: menuController.selectedIndex.value == 1,
onTap: () {
menuController.selectTab(1);
},
),
ListTile(
title: const Text('Page 3'),
selected: menuController.selectedIndex.value == 2,
onTap: () {
menuController.selectTab(2);
},
),
],
),
),
),
Expanded(
flex: 5,
child: Container(color: Colors.white, child: const SizedBox() //
Obx(
() {
switch (menuController.selectedIndex.value) {
case 0:
return const Page1();
case 1:
return const Page2();
case 2:
return const Page3();
default:
return Container();
return Container();
}
},
),
),
),
],
),
// ),
);
}
}
以防万一有人想说“告诉我们你试过什么”。到目前为止,我已经尝试了很多东西。如果你知道如何做到这一点,请指出正确的方向。
您首先需要了解
Navigator
小部件将所有打开的路线列为“兄弟姐妹”,当您确实有一个 Page1
是导航器中的一条路线时,您可以认为它是 的孩子Navigator
当您打开该Navigator
的另一条路线时,它会作为该Page2
的兄弟姐妹打开。
Navigator 小部件是一个
InheritedWidget
,这意味着使用Getx 进行路由会禁用与BuildContext
的交互
它(以Get.to(Widget())
为例),你需要避免/不使用。
---> Page1---> Button1 ( that opens Page2 route )
Navigator => |
---> Page2
通过理解这一点,如果您在
Page1
中有一个菜单栏,它不会在 Page2
中显示,因为它是一条不同的路线,这就是为什么您会在您的应用程序中出现这种行为。
Navigator
小部件是一个InheritedWidget
,这意味着要从您的应用程序访问它,我们使用Navigator.of(context)
,这会启动默认包含在我们的Navigator
中的MaterialApp
小部件应用程序
为了拥有一个始终显示持久导航栏的应用程序,即使我们确实导航到应用程序中的嵌套路由,我们需要创建一个新的
Navigator
小部件,它位于具有菜单栏的屏幕前面。
---> Page1 ---> Button1 ( that opens Page2 route )
ScreenWithMenuBar -> Navigator => |
---> Page2
所以当您在页面之间导航时,总是会显示
ScreenWithMenuBar
。
几天来我一直在使用 GoRouter,并且仍在尝试理解一些关键概念,但这就是我设法实现嵌套导航的方式。
== 路由器 ==
ShellRoute
因为它返回RootLayout
我有一个AdaptiveNavigator
并期待一个孩子是你想要显示的屏幕_parentKey
到parentNavigatorKey
CupertinoPage
,MaterialPage
或 CustomTransitionPage
在这里你可以添加自己的魔法final _parentKey = GlobalKey<NavigatorState>();
final _shellKey = GlobalKey<NavigatorState>();
final appRouter = GoRouter(
// debugLogDiagnostics: true,
navigatorKey: _parentKey,
initialLocation: Routes.login,
redirect: (BuildContext context, GoRouterState state) {
final bool isAuthenticated =
context.read<AuthenticationBloc>().isAuthenticated;
if (state.subloc == Routes.register && !isAuthenticated) {
return Routes.register;
} else if (state.subloc == Routes.login && !isAuthenticated) {
return Routes.login;
} else if (state.subloc == Routes.login && isAuthenticated) {
return Routes.dashboard;
} else if (!isAuthenticated) {
return Routes.login;
} else {
return null;
}
},
// TODO => create page not found screen
// * this is mainly used for the web version when you hardlink to an unknown page
errorBuilder: ((context, state) => const Scaffold(
body: Center(
child: Text(
'This my custom errorpage',
textAlign: TextAlign.center,
),
),
)),
routes: [
ShellRoute(
// * => shell route for the navigation bar
navigatorKey: _shellKey,
builder: (context, state, child) {
return RootLayout(child: child);
},
routes: [
GoRoute(
name: 'dashboard',
path: Routes.dashboard,
pageBuilder: (context, state) => NoTransitionPage(
key: state.pageKey,
child: const DashboardPage(),
),
),
// * => documents route + sub routes
GoRoute(
name: 'documents',
path: Routes.documents,
pageBuilder: (context, state) => NoTransitionPage(
key: state.pageKey,
child: const DocumentsPage(),
),
routes: [
GoRoute(
parentNavigatorKey: _parentKey,
name: 'documentsView',
path: Routes.documentsView,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: ViewDocumentPage(
documentUid: state.params['documentUid'] as String,
),
),
),
GoRoute(
parentNavigatorKey: _parentKey,
name: 'documentsEdit',
path: Routes.documentsEdit,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: EditDocumentPage(
documentUid: state.params['documentUid'] as String,
),
),
),
GoRoute(
parentNavigatorKey: _parentKey,
name: 'documentsAdd',
path: Routes.documentsAdd,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const AddDocumentPage(),
),
),
],
),
// * => websites route + sub routes
GoRoute(
name: 'websites',
path: Routes.websites,
pageBuilder: (context, state) => NoTransitionPage(
key: state.pageKey,
child: const WebsitesPage(),
),
routes: [
GoRoute(
parentNavigatorKey: _parentKey,
name: 'websitesAdd',
path: Routes.websitesAdd,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const AddWebsitePage(),
),
),
],
),
// * => tasks route + sub routes
GoRoute(
name: 'tasks',
path: Routes.tasks,
pageBuilder: (context, state) => NoTransitionPage(
key: state.pageKey,
child: const ListTasksPage(),
),
routes: [
GoRoute(
parentNavigatorKey: _parentKey,
name: 'tasksAdd',
path: Routes.tasksAdd,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const AddTaskPage(),
),
),
],
),
],
),
// * => Auth
GoRoute(
name: 'login',
parentNavigatorKey: _parentKey,
path: Routes.login,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const LoginPage(),
),
),
GoRoute(
name: 'register',
parentNavigatorKey: _parentKey,
path: Routes.register,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const RegisterPage(),
),
),
// * => search
GoRoute(
name: 'search',
parentNavigatorKey: _parentKey,
path: Routes.search,
pageBuilder: (context, state) => CupertinoPage(
key: state.pageKey,
child: const SearchPage(),
),
),
],
);
==根布局==
AdaptiveNavigation
只需检查布局大小,如果是 Desktop,它将显示 NavigationRail
或 Mobile BottomNavigationBar
_Switcher
只是一个在布局大小变化时提供一些基本动画的类class RootLayout extends StatelessWidget {
const RootLayout({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
static const _switcherKey = ValueKey('switcherKey');
@override
Widget build(BuildContext context) {
return AdaptiveNavigation(
child: _Switcher(
key: _switcherKey,
child: child,
),
);
}
}
class _Switcher extends StatelessWidget {
final Widget child;
const _Switcher({
required this.child,
super.key,
});
@override
Widget build(BuildContext context) {
return UniversalPlatform.isDesktop
? child : AnimatedSwitcher(
key: key,
duration: const Duration(milliseconds: 200),
switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
child: child,
);
}
}
总的来说,这里发生的是一个具有 2 个状态的路由器,它有一个带有导航栏的小部件和一个从 GoRouter 向下传递的子小部件。 如果您需要更多详细信息,请告诉我。