如何在 Flutter 中进行嵌套导航
Posted
技术标签:
【中文标题】如何在 Flutter 中进行嵌套导航【英文标题】:How to do nested navigation in Flutter 【发布时间】:2019-09-07 00:46:21 【问题描述】:对于在 Flutter 中解决嵌套导航有什么建议吗?
我想要的是保持一个持久的 BottomNavigationBar,即使在重定向到新屏幕时也是如此。与 YouTube 类似,底部栏始终存在,即使您更深入地浏览菜单也是如此。
我无法从文档中弄清楚。
到目前为止,我能找到的唯一深入了解我的要求的教程是https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf(source code)。但是,它超级令人困惑。
我现在正在使用
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context)
return Container()
但是,它只是将新的小部件推到整个堆栈上,覆盖了 BottomNavigationBar。
任何提示将不胜感激!
【问题讨论】:
【参考方案1】:这是一个简单的例子,它甚至支持使用标签栏弹出到第一个屏幕。
import 'package:flutter/material.dart';
import '../library/screen.dart';
import '../playlists/screen.dart';
import '../search/screen.dart';
import '../settings/screen.dart';
class TabsScreen extends StatefulWidget
@override
_TabsScreenState createState() => _TabsScreenState();
class _TabsScreenState extends State<TabsScreen>
int _currentIndex = 0;
final _libraryScreen = GlobalKey<NavigatorState>();
final _playlistScreen = GlobalKey<NavigatorState>();
final _searchScreen = GlobalKey<NavigatorState>();
final _settingsScreen = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context)
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: <Widget>[
Navigator(
key: _libraryScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => LibraryScreen(),
),
),
Navigator(
key: _playlistScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PlaylistsScreen(),
),
),
Navigator(
key: _searchScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SearchScreen(),
),
),
Navigator(
key: _settingsScreen,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => SettingsScreen(),
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (val) => _onTap(val, context),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.library_books),
title: Text('Library'),
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('Playlists'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
);
void _onTap(int val, BuildContext context)
if (_currentIndex == val)
switch (val)
case 0:
_libraryScreen.currentState.popUntil((route) => route.isFirst);
break;
case 1:
_playlistScreen.currentState.popUntil((route) => route.isFirst);
break;
case 2:
_searchScreen.currentState.popUntil((route) => route.isFirst);
break;
case 3:
_settingsScreen.currentState.popUntil((route) => route.isFirst);
break;
default:
else
if (mounted)
setState(()
_currentIndex = val;
);
【讨论】:
这对我来说效果很好 - 我 generalized this approach 并添加了一些帮助者,例如tabNavigatorOf(context, int tabIndex )
我在保持选项卡状态时遇到问题,但索引堆栈工作得很好,谢谢
这样做后,如何从任何内页推送一个没有底栏的页面?
我在使用这种方法时遇到了问题 - 当我从一个选项卡切换到另一个选项卡然后点击返回按钮时,应用程序会关闭而不是切换回第一个选项卡。另外,如果我进行嵌套导航,例如使用导航器切换到另一个页面,例如图书馆页面,回击也会关闭应用程序。有什么建议吗?
这种方法导致的问题是点击状态栏不会滚动到顶部。【参考方案2】:
这里是持久的 BottomNavigationBar 作为启动器的示例代码:
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
home: MainPage(),
);
class MainPage extends StatelessWidget
final navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context)
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Navigator(
key: navigatorKey,
onGenerateRoute: (route) => MaterialPageRoute(
settings: route,
builder: (context) => PageOne(),
),
),
),
BottomNavigationBar(navigatorKey)
],
),
);
class BottomNavigationBar extends StatelessWidget
final GlobalKey<NavigatorState> navigatorKey;
BottomNavigationBar(this.navigatorKey) : assert(navigatorKey != null);
Future<void> push(Route route)
return navigatorKey.currentState.push(route);
@override
Widget build(BuildContext context)
return Container(
color: Colors.blue,
child: ButtonBar(
alignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text("PageOne"),
onPressed: ()
push(MaterialPageRoute(builder: (context) => PageOne()));
,
),
RaisedButton(
child: Text("PageTwo"),
onPressed: ()
push(MaterialPageRoute(builder: (context) => PageTwo()));
,
)
],
),
);
class PageOne extends StatelessWidget
@override
Widget build(BuildContext context)
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page One"),
RaisedButton(
onPressed: ()
Navigator.of(context).pop();
,
child: Text("Pop"),
),
],
),
);
class PageTwo extends StatelessWidget
@override
Widget build(BuildContext context)
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Page Two"),
RaisedButton(
onPressed: ()
Navigator.of(context).pop();
,
child: Text("Pop"),
),
],
),
);
屏幕录像是这样的
【讨论】:
如果我遗漏了什么,请告诉我。 我根据您的解决方案尝试了一些方法。我在Scaffold
正文中创建了一个Expanded
Column
。我在里面放了一个BottomNavigationBar
,我正在使用GlobalKey<NavigatorState>()
来推送新页面。但是,BottomNavigationBar
页面之间的动画非常不流畅。它没有使用BottomNavigationBar
动画。它只是将一页堆叠在另一页之上(并弄乱了go back
)
我不明白,你想要什么动画?左右滑动切换?
@HarshBhikadia 我尝试了您的解决方案并且效果很好,但是在特定页面上时,例如Page1 和 Page1 有一个 TextField() 小部件,当单击该 TextField 小部件时,它会自动打开默认路由“/”。基本上,它会重新打开默认路由返回的页面。有什么办法解决这个问题?
似乎嵌套导航器在热重载时失去了状态,我从初始路线开始。还有其他人有这种行为吗?开发时非常讨厌......以上是关于如何在 Flutter 中进行嵌套导航的主要内容,如果未能解决你的问题,请参考以下文章