这里还有一个类似的未解决问题:FullscreenDialog screen not covering bottomnavigationbar
但我想提供更多背景信息,以了解这是否有助于找到解决方案。
我们将从我的main.dart
开始,在构建一个MaterialApp
来构建自定义DynamicApp
。这是重要的东西:
Widget build(BuildContext context) {
var _rootScreenSwitcher = RootScreenSwitcher(key: switcherKey);
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.green,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
builder: (context, child) {
return DynamicApp(
navigator: locator<NavigationService>().navigatorKey,
child: child,
switcher: _rootScreenSwitcher,
);
},
navigatorKey: locator<NavigationService>().navigatorKey,
onGenerateRoute: (routeSettings) {
switch (routeSettings.name) {
case SettingsNavigator.routeName:
return MaterialPageRoute(
builder: (context) => SettingsNavigator(),
fullscreenDialog: true);
default:
return MaterialPageRoute(builder: (context) => SettingsNavigator());
}
},
home: _rootScreenSwitcher,
);
}
我的DynamicApp
像这样设置根Scaffold
:
Widget build(BuildContext context) {
return Scaffold(
drawer: NavigationDrawer(
selectedIndex: _selectedIndex,
drawerItems: widget.drawerItems,
headerView: Container(
child: Text('Drawer Header'),
decoration: BoxDecoration(color: Colors.blue),
),
onNavigationItemSelect: (index) {
onTapped(index);
return true; // true means that drawer must close and false is Vice versa
},
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
onTapped(index);
},
currentIndex: _selectedIndex,
items: bottomNavBarItems,
showUnselectedLabels: false,
),
body: widget.child,
);
}
DynamicApp
的子级是名为RootScreenSwitcher
的小部件,它是IndexedStack
,可控制屏幕从我的BottomNavigationBar
的切换,以及在Drawer
。这是RootScreenSwitcher
:
class RootScreenSwitcherState extends State<RootScreenSwitcher> {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
set currentIndex(index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: false,
child: IndexedStack(
index: _currentIndex,
children: menuItems.map<Widget>((MenuItem item) {
return item.widget;
}).toList(),
),
),
);
}
void switchScreen(int index) {
setState(() {
_currentIndex = index;
});
}
}
应用程序的每个主要部分都有自己的Navigator
和根屏幕。一切正常,我对整体导航结构感到满意。每个根屏幕都有其自己的AppBar
,但是全局Drawer
和BottomNavigationBar
是在DynamicApp
中处理的,因此我不必继续在另一个{{1 }}屏幕。
因此,接着开始介绍该应用程序的其他部分,这些部分不受底部标签栏的服务,可以通过Scaffold
或其他操作按钮进行显示。这些新部分中的每一个都必须是模式Drawer
屏幕,以便它们从底部向上滑动,但是具有自己的导航和支架。
我的问题是,当我导航到fullscreenDialog
屏幕时,它是从SettingsNavigator
的后面向上滑动的,而不是在所有内容的顶部。这是BottomNavigationBar
的{{1}}方法:
onGenerateRoute
我是Flutter的新手,还不太了解路由如何与上下文一起使用,因此我想知道调用MaterialApp
的{{1}}方法的屏幕上下文是否成为主要对象构建上下文,因此不在小部件树的顶部吗?
gif此处:https://www.dropbox.com/s/nwgzo0q28cqk61p/FlutteRModalProb.gif?dl=0
这是树状结构,显示设置屏幕的脚手架已放置在onGenerateRoute: (routeSettings) {
switch (routeSettings.name) {
case SettingsNavigator.routeName:
return MaterialPageRoute(
builder: (context) => SettingsNavigator(),
fullscreenDialog: true);
default:
return MaterialPageRoute(builder: (context) => SettingsNavigator());
}
}
navigateTo
内。模态需要坐在Navigator
上方。
有人能对此有所启发吗?
更新:我尝试为标签栏屏幕创建和共享唯一的DynamicApp
键,然后“设置”页面具有不同的键。没关系。我现在想知道是不是Scaffold
拥有相同的父母。
UPDATE UPDATE:
昨晚我取得了突破,这使我意识到以目前的方式使用嵌入式脚手架是不可能的。问题是我有一个名为DynamicApp
的根脚手架,它可以将我的ScaffoldState
和BuildContext
保留下来,但是将其他DynamicApp
页加载到正文中意味着这些模式被插入其中正文和Drawer
之后。要解决该问题,您必须将BottomNavigationBar
子类化,并在每个Scaffold
中引用它;这意味着封装所有业务逻辑,以便在与导航交互时使用BottomNavigationBar
来更改状态。基本上,Flutter会在您的体系结构上强制关注点分离,我认为这是一件好事。完成所有额外工作后,我将为您提供一个更好的答案。
答案 0 :(得分:0)
经过数小时的努力,我试图将ScaffoldState
的密钥传递出去,而Navigators
的答案是将BottomNavigationBar
构建到每个Scaffold
中。但是要做到这一点,我不得不改变我的架构的工作方式……变得更好!现在,我有一个BottomNavigationBar
和RootScreenSwitcher
来监听AppState
ChangeNotifier
的更新,并在页面索引更改时自行重建。因此,我只需要在一个地方更改状态即可使应用程序自动适应。这是AppState
类:
import 'package:flutter/material.dart';
class AppState extends ChangeNotifier {
int _pageIndex = 0;
int get pageIndex {
return _pageIndex;
}
set setpageIndex(int index) {
_pageIndex = index;
notifyListeners();
}
}
这是我的自定义BottomNavigationBar
,名为AppBottomNavigationBar
:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppBottomNavigationBar extends StatefulWidget {
AppBottomNavigationBar({Key key}) : super(key: key);
@override
_AppBottomNavigationBarState createState() => _AppBottomNavigationBarState();
}
class _AppBottomNavigationBarState extends State<AppBottomNavigationBar> {
@override
Widget build(BuildContext context) {
var state = Provider.of<AppState>(
context,
);
int currentIndex = state.pageIndex;
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex ?? 0,
items: bottomNavBarItems,
showUnselectedLabels: false,
onTap: (int index) {
setState(() {
state.setpageIndex = index;
});
},
);
}
}
因此,现在在我的其他Scaffold
页中,我只需要包括以下行以确保BottomNavigationBar is in the
Scaffold`:
bottomNavigationBar: AppBottomNavigationBar(),
这意味着绝对最小的样板。
我将DynamicApp
类的名称更改为AppRootScaffold
,现在它包含一个Scaffold
,Drawer
,然后将Scaffold
的主体设置为{ {1}}:
RootScreenSwitcher
要简化此架构,我还有很多工作要做,但这绝对是更好的选择。
更新:
您能发现新的class RootScreenSwitcher extends StatelessWidget {
RootScreenSwitcher({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
var state = Provider.of<AppState>(
context,
);
int currentIndex = state.pageIndex;
return SafeArea(
top: false,
child: IndexedStack(
index: currentIndex ?? 0,
children: menuItems.map<Widget>((MenuItem item) {
return item.widget;
}).toList(),
),
);
}
}
体系结构的问题吗?
这仍然不是很好。具有讽刺意味的是,我需要回到根Scaffold
中的BottomNavigationBar
才能正常工作。但是随后,模态将不会再次出现在栏的顶部。