在iOS中,当我们推送到新的ViewController时,我们有一个UITabBarController永久保留在屏幕的底部。
在Flutter中,我们有一个脚手架的底部导航栏。但是,与iOS不同,当我们Navigator.push
到新屏幕时,这个bottomNavigationBar会消失。
在我的应用中,我希望满足此要求:主屏幕有一个bottomNavigationBar
,其中包含2个项目( a & b ),显示屏幕 A & 乙。默认情况下,会显示屏幕 A 。在屏幕 A 内,有一个按钮。点按该按钮Navigator.push
以屏幕 C 。现在在屏幕 C 中,我们仍然可以看到bottomNavigationBar
。点按项目 b ,我转到屏幕 B 。现在在屏幕 B 中,点击bottomNavigationBar
中的项目 a ,然后返回到屏幕 C (不是 A < / em>, A 当前位于导航层次结构中的 C 之下。
我该怎么做?谢谢,伙计们。
编辑:我要包括一些图片用于演示:
屏幕A. Screen A
点击转到C 按钮,按到屏幕C Screen C
点按底部导航栏中的右侧项,转到屏幕B. Screen B
答案 0 :(得分:10)
截图:
起点:
void main() => runApp(MaterialApp(home: HomePage()));
HomePage
[BottomNavigationBar
+ Page1
]
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.orange,
items: [
BottomNavigationBarItem(icon: Icon(Icons.call), label: 'Call'),
BottomNavigationBarItem(icon: Icon(Icons.message), label: 'Message'),
],
),
body: Navigator(
onGenerateRoute: (settings) {
Widget page = Page1();
if (settings.name == 'page2') page = Page2();
return MaterialPageRoute(builder: (_) => page);
},
),
);
}
}
第一页:
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page1')),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.pushNamed(context, 'page2'),
child: Text('Go to Page2'),
),
),
);
}
}
第二页:
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) => Scaffold(appBar: AppBar(title: Text('Page2')));
}
答案 1 :(得分:7)
tl; dr:将CupertinoTabBar与CupertinoTabScaffold
一起使用问题不在于Flutter,而在于用户体验,就像RémiRousselet所提到的那样。
事实证明,Material Design不建议层次结构中的子页面访问底部导航栏。
但是,iOS人机界面指南推荐此功能。因此,要使用此功能,我必须调整Cupertino小部件而不是Material小部件。具体来说,在main中,返回包含WidgetsApp/MaterialApp
的{{1}}。使用CupertinoTabScaffold
实施标签栏,每个屏幕都是CupertinoTabBar
。
答案 2 :(得分:5)
您需要使用路线创建MaterialApp,并使BottomNavigationBar成为它的兄弟。然后使用传递给BottomNavigationBar的MaterialApp.navigatorKey进行导航。
https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5
答案 3 :(得分:3)
使用persistent_bottom_nav_bar包,您可以维护单个标签的导航路径,并且当用户也导航到任何屏幕时,BottomNavigationBar都不会消失。
答案 4 :(得分:2)
您实际上可以在体内放置一个占位符 这样的结构
- AppBar
- body (dynamic content from placeholder)
- BottomNavigationBar
然后您将拥有另一个班级作为占位符 因此,每次您点击BottomNavigationBar时,都会刷新身体的内容
我发现的一个例子在这里 https://willowtreeapps.com/ideas/how-to-use-flutter-to-build-an-app-with-bottom-navigation
,但这里太复杂了,对我不起作用 https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5
答案 5 :(得分:2)
方法1:如果您只想保留BottomNavigationBar
,请尝试使用this。
选项2:将CupertinoTabBar
用于静态BottomNavigationBar
。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mqttdemo/Screen2.dart';
import 'package:mqttdemo/Screen3.dart';
import 'Screen1.dart';
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex;
List<Widget> _children;
@override
void initState() {
_currentIndex = 0;
_children = [
Screen1(),
Screen2(),
Screen3(),
];
super.initState();
}
@override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Screen 1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Screen 2"),
),
BottomNavigationBarItem(
icon: Icon(Icons.home), title: Text("Screen 3")),
],
),
tabBuilder: (BuildContext context, int index) {
return CupertinoTabView(
builder: (BuildContext context) {
return SafeArea(
top: false,
bottom: false,
child: CupertinoApp(
home: CupertinoPageScaffold(
resizeToAvoidBottomInset: false,
child: _children[_currentIndex],
),
),
);
},
);
}
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
从Screen3导航到screen4,如下所示:
class Screen3 extends StatefulWidget {
@override
_Screen3State createState() => _Screen3State();
}
class _Screen3State extends State<Screen3> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: Center(
child: RaisedButton(
child: Text("Click me"),
onPressed: () {
Navigator.of(context, rootNavigator: false).push(MaterialPageRoute(
builder: (context) => Screen4(), maintainState: false));
},
),
),
);
}
}
答案 6 :(得分:1)
我认为这样做的#right方式是将BottomNavigationBar包含在Hero中并且在两种情况下使用相同的标记。这样,当页面之间的动画发生时,它们将被排除。
这是我可以做的一个简短的示例,但我强烈建议清理它,即使用小部件而不是大块构建传递英雄字符串,为BottomNavigationBar制作自己的小部件。
请注意,在英雄转换期间,它至少会在我的手机上overflow by 0.0000191 pixels
,但在发布模式下,这不是一个我不会想到的问题。
import&#39; package:flutter / material.dart&#39;;
void main() => runApp(new MaterialApp(
home: new Builder(
builder: (context) => new Scaffold(
bottomNavigationBar: new Hero(
tag: "bottomNavigationBar",
child: new BottomNavigationBar(items: [
new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
]),
),
body: new SafeArea(
child: new Container(
constraints: new BoxConstraints.expand(),
color: Colors.green,
child: new Column(
children: <Widget>[
new RaisedButton(
child: new Text("Press me"),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Scaffold(
bottomNavigationBar: new Hero(
tag: "bottomNavigationBar",
child: new BottomNavigationBar(items: [
new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
]),
),
body: new SafeArea(
child: new Container(
constraints:
new BoxConstraints.expand(),
color: Colors.red,
child: new Column(
children: <Widget>[
new RaisedButton(
onPressed: () =>
Navigator.pop(context),
child: new Text("Back"),
)
],
),
),
),
)));
})
],
),
),
),
),
),
));
我不知道hero
系统处理多个英雄等有多好,如果你说想要为导航栏设置动画,这可能效果不太好。
还有另一种方法可以让你为底部导航栏设置动画;它实际上是一个已经回答的问题:Flutter: Hero transition + widget animation at the same time?
答案 7 :(得分:1)
实现此目的的另一种方法(尽管不是很好的做法)是将材质应用程序嵌套在支架的主体中。并在那里处理所有“子导航”。
因此,您的层次结构将如下所示
<div class="text-block">
<p>basically I want to implement a banner CTA block which would always go over the previous and next DIVs but not create a white, empty spacing. I want to implement this in fluid way, irrespective of the height of the actual CTA banner block.</p>
<div class="cta">basically I want to implement a banner CTA block which would always go over the previous and next DIVs but not create a white, empty spacing. I want to implement this in fluid way, irrespective of the height of the actual CTA banner block.</div>
</div>
我已经尝试过了,效果很好。不幸的是,我现在无法发布源代码。
答案 8 :(得分:0)
您可以在Navigator
小部件中创建Stack
小部件,以将BottomNavigationBar
用于标签的内部导航。您可以使用WillPopScope
处理Android的后退按钮以弹出标签的内部屏幕。另外,双击底部导航项以弹出选项卡的所有内部屏幕。
我已经创建了一个Sample app 为此。
希望获得帮助!
答案 9 :(得分:0)
我创建了一个小巧,易于使用的软件包,可以让您实现这种效果CustomNavigator 并在Medium上撰写了有关它的教程here
答案 10 :(得分:0)
您可以使用auto_route包的嵌套路由,这就是我实现它的方式。如果有人想参考auto_route
,请点击这里实现此目标的另一种方法是使用get软件包。
不建议使用嵌套路由,因为它们在某些情况下会造成内存泄漏。