flutter - Flutter:我有一个持久的自定义覆盖,里面有一个导航器。如何根据导航页面显示/隐藏覆盖?
问题描述
我想实现一个使用auto_route包在其中的页面之间导航的覆盖。我希望覆盖在页面之间保持不变,所以如果搜索栏或抽屉打开,一旦你推送或弹出页面,它的外观就会保持不变。
问题来自下一个要求:我希望根据要导航到的页面隐藏或显示它。也就是说,如果我在显示覆盖的“Page1”中并且执行“Navigator.pushNamed(/Page2)”并且Page2 隐藏了覆盖,则覆盖隐藏。如果我执行“Navigator.pop()”,它会返回到“Page1”,再次显示叠加层。
因为页面是由“ExtendedNavigator”类给出的,并且 Overlay 本身是首先构建的,所以我无法在构建时知道 Navigator 导航到的位置或当前正在构建的页面。我需要事先知道这些信息才能相应地隐藏或显示叠加层。
我尝试了几种解决方案均无济于事。
第一种方法
在需要渲染的每个页面中实例化 Overlay,并使用 Inherited Widget 来维护状态。
问题:无法保持所有覆盖的状态同步。键不能重复使用,也不能复制它们的状态(例如,对于 ScaffoldState)。因此,每当其中一个叠加层发生更改时,我都应该将相同的更改复制到所有其他叠加层,以相应地更改它们的状态。此外,不能实例化具有特定状态的 Scaffold。
此解决方案的代码大致如下所示:
class Page1 extends StatefulWidget {
Page1 ({
Key key,
}) : super(key: key);
@override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
@override
Widget build(BuildContext context) {
return Overlay(
child: (...)
);
}
}
class Page2 extends StatefulWidget {
Page2 ({
Key key,
}) : super(key: key);
@override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
@override
Widget build(BuildContext context) {
return Overlay(
child: (...)
);
}
}
class OverlayData extends InheritedWidget {
bool _hidden = true;
OverlayData({
Key key,
@required Widget child,
}) : super(key: key, child: child);
static OverlayData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<OverlayData>();
}
void setHiddenTo(bool value) {
_hidden = value;
print("Overlay is now in state: " + value.toString());
}
bool isHidden() {
return _hidden;
}
@override
bool updateShouldNotify(OverlayData oldWidget) =>
_hidden != oldWidget._hidden;
}
//// Main
void main() {
runApp(App());
}
class App extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AppState();
}
class _AppState extends State<App> {
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: ExtendedNavigator.builder<router.Router>(
router: router.Router(),
builder: (context, extendedNav) => Theme(
data: ThemeData(
brightness: Brightness.light,
),
child: OverlayData(child: Overlay(child: extendedNav)),
),
),
color: StyleValues.blue,
);
}
}
第二种方法
每次手动推送/弹出到另一个页面时,使用其 GlobalKey 手动触发覆盖的隐藏/显示。推送页面时,我可以使用 [Guards] 检查我正在导航到的页面是否显示或隐藏了叠加层,并使用叠加层的键触发更改。
问题:当我使用 Android 按钮弹出时,我不知道要弹出到哪个页面并相应地触发更改。即使我用“WillPopScope”包裹我的叠加层,我仍然不知道我要弹出哪个页面,所以它没有用。
这个代码看起来大概是这样的:
/// Main
void main() {
runApp(App());
}
class App extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AppState();
}
class _AppState extends State<App> {
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: ExtendedNavigator.builder<router.Router>(
router: router.Router(),
guards: [HideGuard(), ShowGuard()]
builder: (context, extendedNav) => Theme(
data: ThemeData(
brightness: Brightness.light,
),
child: Overlay(child: extendedNav),
),
),
color: StyleValues.blue,
);
}
}
/// Overlay
class Overlay extends StatefulWidget {
final Widget body;
Overlay({this.body});
@override
OverlayState createState() => OverlayState(this.body);
}
class OverlayState extends State<Overlay> {
final Widget body;
bool _hidden;
OverlayState(this.body);
void setHiddenTo(bool value){
if (value != _hidden)
{
SetState(() {
_hidden = value;
})
}
}
@override
Widget build(BuildContext context) {
/// Here I check if it is hidden or not
if (_hidden) {
return body;
}
return Scaffold(
appBar: PreferredSize(
child: AppBar(...)
),
/// Bottom Bar
bottomNavigationBar: (...),
/// Content (Second Scaffold to render Drawer behind AppBar and BottomBar)
body: Scaffold(
key: _scaffoldKey,
drawer: Drawer(
child: (...)
),
body: body,
),
);
}
}
/// The router
@MaterialAutoRouter(routes: <AutoRoute>[
MaterialRoute(path: PageRoutes.page1, page: Page1, initial: true, guards: [HideGuard]),
MaterialRoute(path: PageRoutes.page2, page: Page2, guards: [ShowGuard]),
MaterialRoute(path: PageRoutes.page3, page: Page3, guards: [ShowGuard]),
])
class $Router {}
/// The Key (in a separate class)
class Keys{
GlobalKey<OverlayState> overlayKey = GlobalKey<OverlayState>();
}
/// A guard
class ShowGuard extends RouteGuard {
@override
Future<bool> canNavigate(
ExtendedNavigatorState navigator, String routeName, Object arguments) async {
Keys.OverlayKey.currentState.setHiddenTo(false);
}
}
class HideGuard extends RouteGuard {
@override
Future<bool> canNavigate(
ExtendedNavigatorState navigator, String routeName, Object arguments) async {
Keys.OverlayKey.currentState.setHiddenTo(true);
}
}
我应该如何处理这个问题?在构建覆盖之前,有什么方法可以检索将要推送/弹出的当前页面?有没有办法让几个小部件共享相同的状态?预先感谢您的帮助。
解决方案
推荐阅读
- javascript - 从 for 循环中填充对象列表的速记方法
- java - 在 Eclipse 中运行和进入项目文件夹并使用 .jar 之间的区别
- javascript - 如何单击外部关闭菜单框 JavaScript
- graphql - Apollo GraphQl Query 从两个文档中获取数据
- excel - Excel VBA根据变量选择范围
- javascript - 鼠标悬停区域时更改图像的 src
- c# - C# 中带有 IExpandCollapseProvider 的自定义 FrameWorkElementAutomationPeer 的内存泄漏
- angular - 在 Angular 5 应用程序中构建 ng 后 Typekit 字体 CSS 未加载
- hibernate - 休眠 OneToMany 列表重新加载
- influxdb - influxdb:从内存类型升级后 tsi1 索引的使用不明确