Flutter Navigator 2.0:带抽屉导航的命名路由
Flutter Navigator 2.0: Named routes with Drawer Navigation
我正在一个遵循 Flutter Navigator 2.0 的应用程序上开发一个简单的导航,以便更好地支持路线。 Navigator 2.0 的一个优点是您可以对屏幕导航进行精细控制,并更好地支持网络。
这是我要实现的目标的图表。
我可以毫无问题地从登录屏幕导航到“主页”。主页有一个 Drawer
,默认显示 HomeScreen。我的问题是我不确定如何使用 Navigator 2.0 从抽屉中正确显示 HomeScreen 和 ProfileScreen。在此之后 guide 演示了将屏幕推送到导航器堆栈并使用 RouterDelegate
跟踪路线。这样做会将新屏幕推送到堆栈。
该应用似乎运行良好,但我只是删除了过渡动画。您会注意到,甚至在 Drawer 完成其关闭动画之前,就绘制了一个新屏幕。由于主页面被推入堆栈,整个抽屉小部件被再次绘制。
这个 guide 在抽屉上显示屏幕只替换同一屏幕上的小部件。我目前正在做的是每次导航到 HomeScreen 和 ProfileScreen 时我都在重建主页
这是主页的样子。 currentPage
更新为显示 HomeScreen 和 ProfileScreen 的 Widget。
late Widget currentPage;
@override
Widget build(BuildContext context) {
currentPage = HomeScreen();
return Scaffold(
appBar: AppBar(title: Text(title)),
body: currentPage,
drawer: Drawer(
child: ListView(
children: <Widget>[
// ...
ListTile(
title: Text('Home'),
onTap: () {
widget.navHome();
Navigator.pop(context);
},
),
ListTile(
title: Text('Profile'),
// ...
),
],
),
),
);
}
我的 RouterDelegate 上的 Navigator 有这个设置。
Navigator(
key: navigatorKey,
transitionDelegate: NoAnimationTransitionDelegate(),
pages: [
if (show404)
MaterialPage(
key: ValueKey('UnknownPage'),
child: UnknownScreen(),
)
else if (page == Pages.home)
MaterialPage(
key: ValueKey('HomePage'),
child: HomePage(
title: 'Home',
handleLogout: _logOut,
navHome: _navHome,
navProfile: _navProfile,
currentScreen: HomeScreen(username: username),
),
)
else if (page == Pages.profile)
MaterialPage(
key: ValueKey('ProfilePage'),
child: HomePage(
title: 'Profile',
handleLogout: _logOut,
navHome: _navHome,
navProfile: _navProfile,
currentScreen: ProfileScreen(),
),
)
else // username is null, no user logged in
MaterialPage(
key: ValueKey('LoginPage'),
child: LoginPage(
title: 'Login',
onTapped: _handleLogin,
),
),
]
)
函数作为参数传递以更新 RouterDelegate 中的路由。
enum Pages { login, home, profile }
class PageRouterDelegate extends RouterDelegate<PageRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<PageRoutePath> {
// ... Other lines omitted for simplicity
void _handleLogin(String username, String password) {
// TODO Auth
this.page = Pages.home;
notifyListeners();
}
void _navHome() {
this.page = Pages.home;
notifyListeners();
}
void _navProfile() {
this.page = Pages.profile;
notifyListeners();
}
void _logOut() {
this.page = Pages.login;
notifyListeners();
}
}
有什么方法可以显示抽屉中的不同屏幕并使用 Navigator 2.0 跟踪它们的路线有什么建议吗?
更新:
我找到了这个问题的解决方案,但它需要使用两个导航器。将键分配给导航器有助于我们管理它们。
第二个导航器在主页上有一个抽屉。这使我们能够在不重建整个屏幕的情况下浏览不同的页面。这种方法的注意事项是只有 mainNavigator 上的路由才会显示在 Web 上的地址栏上。
如果您对如何解决这个问题有其他建议,请告诉我。
这不是一个直接的答案,只是一个如何解决这个问题的参考。
Flutter 最近更新了 routing example and in that example, they use InheritedWidget with ChangeNotifier to push new route to route delegate like RouteStateScope.of(context)!.go('/book/${book.id}');
,同时保持路由状态处于活动状态。
由于需要编写大量样板代码,我暂时放弃了使用 Navigator 2.0。我退后一步,使用带有命名路由的 Navigator 1.0。 Navigator 2.0仍然可以应用同样的原理
我现在所做的不是在主导航器上构建不同的屏幕,而是重建相同的屏幕 (NavigatorPage) 以节省资源并为我想导航到的屏幕传递参数。
final _mainNavigatorKey = GlobalKey<NavigatorState>();
...
MaterialApp(
title: 'Navigator Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
navigatorKey: _mainNavigatorKey,
routes: {
/// [title] updates the title on the main AppBar
/// [route] NavigatorPage Router depends on route defined on this parameter
/// [showDrawer] show/hide main AppBar drawer
Nemo.home: (context) => NavigatorPage(
title: 'Home',
route: Nemo.home,
navigatorKey: _mainNavigatorKey,
showDrawer: true,
),
Nemo.post: (context) => NavigatorPage(
title: 'Post',
route: Nemo.post,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.profile: (context) => NavigatorPage(
title: 'Profile',
route: Nemo.profile,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.settings: (context) => NavigatorPage(
title: 'Settings',
route: Nemo.settings,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
},
);
NavigatorPage 包含我们可以更新标题的主 AppBar 和 display/hide LinearProgressBar。从那里,我们可以通过使用 _mainNavigatorKey
作为我们的导航器检查参数中传递的路由来导航到所需的屏幕。
Navigator(
// key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
// switch (settings.name) {
switch (widget.route) {
/// Default page displayed on Home Screen
case Nemo.home:
builder = (BuildContext context) => _homePage();
break;
case Nemo.post:
builder = (BuildContext context) => _postPage();
break;
case Nemo.profile:
builder = (BuildContext context) => _profilePage();
break;
case Nemo.settings:
builder = (BuildContext context) => _settingsPage();
break;
default:
builder = (BuildContext context) => const UnknownPage();
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
我们仍在重建主导航器上的页面。导航抽屉关闭时卡顿动画的解决方法是添加至少 300 毫秒的延迟,以等待动画完成后再执行导航。您可以根据需要调整延迟。
ListTile(
title: const Text('Home'),
onTap: () {
// Close the drawer
Navigator.pop(context);
/// [drawerDelay] gives time to animate the closing of the Drawer
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!.pushNamed(Nemo.home);
});
},
),
演示
示例代码
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final _mainNavigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Navigator Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// home: const NavigatorPage(title: 'Flutter Demo Home Page'),
navigatorKey: _mainNavigatorKey,
routes: {
/// [title] updates the title on the main AppBar
/// [route] NavigatorPage Router depends on route defined on this parameter
/// [showDrawer] show/hide main AppBar drawer
Nemo.home: (context) => NavigatorPage(
title: 'Home',
route: Nemo.home,
navigatorKey: _mainNavigatorKey,
showDrawer: true,
),
Nemo.post: (context) => NavigatorPage(
title: 'Post',
route: Nemo.post,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.profile: (context) => NavigatorPage(
title: 'Profile',
route: Nemo.profile,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.settings: (context) => NavigatorPage(
title: 'Settings',
route: Nemo.settings,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
},
);
}
}
class NavigatorPage extends StatefulWidget {
const NavigatorPage(
{Key? key,
required this.title,
required this.route,
required this.navigatorKey,
required this.showDrawer})
: super(key: key);
final String title;
final String route;
final bool showDrawer;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<NavigatorPage> createState() => _NavigatorPageState();
}
class _NavigatorPageState extends State<NavigatorPage> {
// final _navigatorKey = GlobalKey<NavigatorState>();
/// Drawer delay let's us have the Navigation Drawer close first
/// before the navigating to the next Screen
int drawerDelay = 300;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: widget.showDrawer
? Drawer(
/// TODO return null to hide Drawer if in Login/Registration page
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Home'),
onTap: () {
// Close the drawer
Navigator.pop(context);
/// [drawerDelay] gives time to animate the closing of the Drawer
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!.pushNamed(Nemo.home);
});
},
),
ListTile(
title: const Text('Profile'),
onTap: () {
// Close the drawer
Navigator.pop(context);
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!
.pushNamed(Nemo.profile);
});
},
),
ListTile(
title: const Text('Settings'),
onTap: () {
// Close the drawer
Navigator.pop(context);
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!
.pushNamed(Nemo.settings);
});
},
),
],
),
)
: null,
body: Navigator(
// key: _navigatorKey,
/// initialRoute needs to be set to '/'
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
// switch (settings.name) {
switch (widget.route) {
/// Default page displayed on Home Screen
case Nemo.home:
builder = (BuildContext context) => _homePage();
break;
case Nemo.post:
builder = (BuildContext context) => _postPage();
break;
case Nemo.profile:
builder = (BuildContext context) => _profilePage();
break;
case Nemo.settings:
builder = (BuildContext context) => _settingsPage();
break;
default:
builder = (BuildContext context) => const UnknownPage();
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
);
}
Widget _homePage() =>
HomePage(title: 'Home', navigatorKey: widget.navigatorKey);
Widget _postPage() =>
PostPage(title: 'Post', navigatorKey: widget.navigatorKey);
Widget _profilePage() =>
ProfilePage(title: 'Profile', navigatorKey: widget.navigatorKey);
Widget _settingsPage() =>
SettingsPage(title: 'Settings', navigatorKey: widget.navigatorKey);
}
class Nemo {
static const home = '/';
static const login = '/login';
static const register = '/register';
static const post = '/post';
static const profile = '/profile';
static const settings = '/settings';
}
/// Constant values for UI elements
class Constants {
static const String webVersion = 'web-0.1.9-dev';
static const double paddingSmall = 8.0;
static const double paddingNormal = 16.0;
static const double heightNormal = 64.0;
static const double heightThreadCard = 72.0;
static const double heightButtonNormal = 42.0;
static const double widthButtonNormal = 160.0;
}
class UnknownPage extends StatefulWidget {
const UnknownPage({Key? key}) : super(key: key);
@override
State<UnknownPage> createState() => _UnknownPageState();
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Home',
),
ElevatedButton(
child: const Text('View Post Page'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class PostPage extends StatefulWidget {
const PostPage(
{Key? key, required this.title, this.id, required this.navigatorKey})
: super(key: key);
final String title;
final String? id;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<PostPage> createState() => _PostPageState();
}
class _PostPageState extends State<PostPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Id from Route: ${widget.id}',
),
],
),
),
);
}
}
class SettingsPage extends StatefulWidget {
const SettingsPage(
{Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Settings',
),
ElevatedButton(
child: const Text('View Details'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class ProfilePage extends StatefulWidget {
const ProfilePage({Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Profile',
),
ElevatedButton(
child: const Text('View Details'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class _UnknownPageState extends State<UnknownPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'404',
),
],
),
),
);
}
}
如果您想要包含基本登录和注册屏幕的样本。我创建了这个模板,您可以查看:https://github.com/omatt/flutter-navdrawer-template
我正在一个遵循 Flutter Navigator 2.0 的应用程序上开发一个简单的导航,以便更好地支持路线。 Navigator 2.0 的一个优点是您可以对屏幕导航进行精细控制,并更好地支持网络。
这是我要实现的目标的图表。
我可以毫无问题地从登录屏幕导航到“主页”。主页有一个 Drawer
,默认显示 HomeScreen。我的问题是我不确定如何使用 Navigator 2.0 从抽屉中正确显示 HomeScreen 和 ProfileScreen。在此之后 guide 演示了将屏幕推送到导航器堆栈并使用 RouterDelegate
跟踪路线。这样做会将新屏幕推送到堆栈。
该应用似乎运行良好,但我只是删除了过渡动画。您会注意到,甚至在 Drawer 完成其关闭动画之前,就绘制了一个新屏幕。由于主页面被推入堆栈,整个抽屉小部件被再次绘制。
这个 guide 在抽屉上显示屏幕只替换同一屏幕上的小部件。我目前正在做的是每次导航到 HomeScreen 和 ProfileScreen 时我都在重建主页
这是主页的样子。 currentPage
更新为显示 HomeScreen 和 ProfileScreen 的 Widget。
late Widget currentPage;
@override
Widget build(BuildContext context) {
currentPage = HomeScreen();
return Scaffold(
appBar: AppBar(title: Text(title)),
body: currentPage,
drawer: Drawer(
child: ListView(
children: <Widget>[
// ...
ListTile(
title: Text('Home'),
onTap: () {
widget.navHome();
Navigator.pop(context);
},
),
ListTile(
title: Text('Profile'),
// ...
),
],
),
),
);
}
我的 RouterDelegate 上的 Navigator 有这个设置。
Navigator(
key: navigatorKey,
transitionDelegate: NoAnimationTransitionDelegate(),
pages: [
if (show404)
MaterialPage(
key: ValueKey('UnknownPage'),
child: UnknownScreen(),
)
else if (page == Pages.home)
MaterialPage(
key: ValueKey('HomePage'),
child: HomePage(
title: 'Home',
handleLogout: _logOut,
navHome: _navHome,
navProfile: _navProfile,
currentScreen: HomeScreen(username: username),
),
)
else if (page == Pages.profile)
MaterialPage(
key: ValueKey('ProfilePage'),
child: HomePage(
title: 'Profile',
handleLogout: _logOut,
navHome: _navHome,
navProfile: _navProfile,
currentScreen: ProfileScreen(),
),
)
else // username is null, no user logged in
MaterialPage(
key: ValueKey('LoginPage'),
child: LoginPage(
title: 'Login',
onTapped: _handleLogin,
),
),
]
)
函数作为参数传递以更新 RouterDelegate 中的路由。
enum Pages { login, home, profile }
class PageRouterDelegate extends RouterDelegate<PageRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<PageRoutePath> {
// ... Other lines omitted for simplicity
void _handleLogin(String username, String password) {
// TODO Auth
this.page = Pages.home;
notifyListeners();
}
void _navHome() {
this.page = Pages.home;
notifyListeners();
}
void _navProfile() {
this.page = Pages.profile;
notifyListeners();
}
void _logOut() {
this.page = Pages.login;
notifyListeners();
}
}
有什么方法可以显示抽屉中的不同屏幕并使用 Navigator 2.0 跟踪它们的路线有什么建议吗?
更新:
我找到了这个问题的解决方案,但它需要使用两个导航器。将键分配给导航器有助于我们管理它们。
第二个导航器在主页上有一个抽屉。这使我们能够在不重建整个屏幕的情况下浏览不同的页面。这种方法的注意事项是只有 mainNavigator 上的路由才会显示在 Web 上的地址栏上。
如果您对如何解决这个问题有其他建议,请告诉我。
这不是一个直接的答案,只是一个如何解决这个问题的参考。
Flutter 最近更新了 routing example and in that example, they use InheritedWidget with ChangeNotifier to push new route to route delegate like RouteStateScope.of(context)!.go('/book/${book.id}');
,同时保持路由状态处于活动状态。
由于需要编写大量样板代码,我暂时放弃了使用 Navigator 2.0。我退后一步,使用带有命名路由的 Navigator 1.0。 Navigator 2.0仍然可以应用同样的原理
我现在所做的不是在主导航器上构建不同的屏幕,而是重建相同的屏幕 (NavigatorPage) 以节省资源并为我想导航到的屏幕传递参数。
final _mainNavigatorKey = GlobalKey<NavigatorState>();
...
MaterialApp(
title: 'Navigator Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
navigatorKey: _mainNavigatorKey,
routes: {
/// [title] updates the title on the main AppBar
/// [route] NavigatorPage Router depends on route defined on this parameter
/// [showDrawer] show/hide main AppBar drawer
Nemo.home: (context) => NavigatorPage(
title: 'Home',
route: Nemo.home,
navigatorKey: _mainNavigatorKey,
showDrawer: true,
),
Nemo.post: (context) => NavigatorPage(
title: 'Post',
route: Nemo.post,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.profile: (context) => NavigatorPage(
title: 'Profile',
route: Nemo.profile,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.settings: (context) => NavigatorPage(
title: 'Settings',
route: Nemo.settings,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
},
);
NavigatorPage 包含我们可以更新标题的主 AppBar 和 display/hide LinearProgressBar。从那里,我们可以通过使用 _mainNavigatorKey
作为我们的导航器检查参数中传递的路由来导航到所需的屏幕。
Navigator(
// key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
// switch (settings.name) {
switch (widget.route) {
/// Default page displayed on Home Screen
case Nemo.home:
builder = (BuildContext context) => _homePage();
break;
case Nemo.post:
builder = (BuildContext context) => _postPage();
break;
case Nemo.profile:
builder = (BuildContext context) => _profilePage();
break;
case Nemo.settings:
builder = (BuildContext context) => _settingsPage();
break;
default:
builder = (BuildContext context) => const UnknownPage();
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
我们仍在重建主导航器上的页面。导航抽屉关闭时卡顿动画的解决方法是添加至少 300 毫秒的延迟,以等待动画完成后再执行导航。您可以根据需要调整延迟。
ListTile(
title: const Text('Home'),
onTap: () {
// Close the drawer
Navigator.pop(context);
/// [drawerDelay] gives time to animate the closing of the Drawer
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!.pushNamed(Nemo.home);
});
},
),
演示
示例代码
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
final _mainNavigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Navigator Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// home: const NavigatorPage(title: 'Flutter Demo Home Page'),
navigatorKey: _mainNavigatorKey,
routes: {
/// [title] updates the title on the main AppBar
/// [route] NavigatorPage Router depends on route defined on this parameter
/// [showDrawer] show/hide main AppBar drawer
Nemo.home: (context) => NavigatorPage(
title: 'Home',
route: Nemo.home,
navigatorKey: _mainNavigatorKey,
showDrawer: true,
),
Nemo.post: (context) => NavigatorPage(
title: 'Post',
route: Nemo.post,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.profile: (context) => NavigatorPage(
title: 'Profile',
route: Nemo.profile,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
Nemo.settings: (context) => NavigatorPage(
title: 'Settings',
route: Nemo.settings,
navigatorKey: _mainNavigatorKey,
showDrawer: true),
},
);
}
}
class NavigatorPage extends StatefulWidget {
const NavigatorPage(
{Key? key,
required this.title,
required this.route,
required this.navigatorKey,
required this.showDrawer})
: super(key: key);
final String title;
final String route;
final bool showDrawer;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<NavigatorPage> createState() => _NavigatorPageState();
}
class _NavigatorPageState extends State<NavigatorPage> {
// final _navigatorKey = GlobalKey<NavigatorState>();
/// Drawer delay let's us have the Navigation Drawer close first
/// before the navigating to the next Screen
int drawerDelay = 300;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: widget.showDrawer
? Drawer(
/// TODO return null to hide Drawer if in Login/Registration page
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text('Drawer Header'),
),
ListTile(
title: const Text('Home'),
onTap: () {
// Close the drawer
Navigator.pop(context);
/// [drawerDelay] gives time to animate the closing of the Drawer
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!.pushNamed(Nemo.home);
});
},
),
ListTile(
title: const Text('Profile'),
onTap: () {
// Close the drawer
Navigator.pop(context);
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!
.pushNamed(Nemo.profile);
});
},
),
ListTile(
title: const Text('Settings'),
onTap: () {
// Close the drawer
Navigator.pop(context);
Timer(Duration(milliseconds: drawerDelay), () async {
widget.navigatorKey.currentState!
.pushNamed(Nemo.settings);
});
},
),
],
),
)
: null,
body: Navigator(
// key: _navigatorKey,
/// initialRoute needs to be set to '/'
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
// Manage your route names here
// switch (settings.name) {
switch (widget.route) {
/// Default page displayed on Home Screen
case Nemo.home:
builder = (BuildContext context) => _homePage();
break;
case Nemo.post:
builder = (BuildContext context) => _postPage();
break;
case Nemo.profile:
builder = (BuildContext context) => _profilePage();
break;
case Nemo.settings:
builder = (BuildContext context) => _settingsPage();
break;
default:
builder = (BuildContext context) => const UnknownPage();
}
return MaterialPageRoute(
builder: builder,
settings: settings,
);
},
),
);
}
Widget _homePage() =>
HomePage(title: 'Home', navigatorKey: widget.navigatorKey);
Widget _postPage() =>
PostPage(title: 'Post', navigatorKey: widget.navigatorKey);
Widget _profilePage() =>
ProfilePage(title: 'Profile', navigatorKey: widget.navigatorKey);
Widget _settingsPage() =>
SettingsPage(title: 'Settings', navigatorKey: widget.navigatorKey);
}
class Nemo {
static const home = '/';
static const login = '/login';
static const register = '/register';
static const post = '/post';
static const profile = '/profile';
static const settings = '/settings';
}
/// Constant values for UI elements
class Constants {
static const String webVersion = 'web-0.1.9-dev';
static const double paddingSmall = 8.0;
static const double paddingNormal = 16.0;
static const double heightNormal = 64.0;
static const double heightThreadCard = 72.0;
static const double heightButtonNormal = 42.0;
static const double widthButtonNormal = 160.0;
}
class UnknownPage extends StatefulWidget {
const UnknownPage({Key? key}) : super(key: key);
@override
State<UnknownPage> createState() => _UnknownPageState();
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Home',
),
ElevatedButton(
child: const Text('View Post Page'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class PostPage extends StatefulWidget {
const PostPage(
{Key? key, required this.title, this.id, required this.navigatorKey})
: super(key: key);
final String title;
final String? id;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<PostPage> createState() => _PostPageState();
}
class _PostPageState extends State<PostPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Id from Route: ${widget.id}',
),
],
),
),
);
}
}
class SettingsPage extends StatefulWidget {
const SettingsPage(
{Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Settings',
),
ElevatedButton(
child: const Text('View Details'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class ProfilePage extends StatefulWidget {
const ProfilePage({Key? key, required this.title, required this.navigatorKey})
: super(key: key);
final String title;
final GlobalKey<NavigatorState> navigatorKey;
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Profile',
),
ElevatedButton(
child: const Text('View Details'),
onPressed: () {
widget.navigatorKey.currentState!.pushNamed(Nemo.post);
},
),
],
),
),
);
}
}
class _UnknownPageState extends State<UnknownPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text(widget.title),
// ),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text(
'404',
),
],
),
),
);
}
}
如果您想要包含基本登录和注册屏幕的样本。我创建了这个模板,您可以查看:https://github.com/omatt/flutter-navdrawer-template