Flutter 嵌套声明式导航
Flutter nested declarative navigation
我正在尝试在 Flutter Material 移动应用程序中实现新的声明式 Navigation
样式。我希望将多个页面堆叠在一起,并能够通过 Scaffolds 后退按钮一个接一个地弹出页面。
示例:一个应用有一个列出早餐食材的主屏幕,一个所有食材的详细信息页面,最后是一个可以从详细信息页面打开的页面,其中列出了喜欢该特定食材的人。只关注导航部分,这是设置:
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!")),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill")),
], onPopPage: (route, result) => route.didPop(result));
}
查看最低工作版本(状态管理和为简单起见而省略的所有其他内容):https://dartpad.dev/3d3451aba0f97016ae8c4b86a8b132bb
导航弹出按预期工作:应用程序从 pages
列表中定义的最后一页开始,通过单击 Scaffold
后退按钮,页面可以一个接一个地弹出,最多顶级页面。到目前为止一切顺利。
现在我想组织小部件,以便导航到“喜欢...的人”子页面不再是 MainNav
的责任,而是成分详细信息页面的责任.我引入了一个中间 Navigation
将详细信息页面推送到页面堆栈,如果需要,还有“喜欢...的人”页面。喜欢:
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("sub"), child: SubNav())
], onPopPage: (route, result) => route.didPop(result));
}
class SubNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!")),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill"))
],
onPopPage: (route, result) => route.didPop(result));
}
查看现场版:https://dartpad.dev/d0c38e2ec443aafc30d9e9f82ef158d9
这里发生的事情是从列表中弹出最后一页后,我们在“脆皮培根”页面上,不可能弹出这一页并返回到“早餐配料”页面列表。我可能遗漏了一些关于导航上下文的基本知识,这不是实现分层、声明式导航的方法。
所以问题是:如何仅使用声明性导航概念将导航任务委托到小部件层次结构中?有没有什么方法可以创建嵌套的 Navigation
小部件并让它们的所有 pages
表现为一个页面层次结构?
下面是一个最小工作示例,显示了您可以用来从内部 SubNav
小部件导航到外部 MainNav
小部件的方法之一:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested declarative navigator',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainNav(),
);
}
}
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("sub"), child: SubNav())
], onPopPage: (route, result) => route.didPop(result));
}
class SubNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!", leading: BackButton(onPressed: () => Navigator.of(context).maybePop()))),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill"))
],
onPopPage: (route, result) => route.didPop(result));
}
class AppShell extends StatelessWidget {
final String title;
final String content;
final Widget leading;
const AppShell({Key key, this.title, this.content, this.leading}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
leading: leading
),
body: Center(child: Text(content)));
}
}
我在 AppShell
小部件的构造函数中添加了一个可选的 leading
参数。这允许我们传入一个 BackButton
小部件,我们可以在 SubNav
小部件中创建自己的小部件。 BackButton
小部件有一个 onPressed
参数,我们可以使用它来 maybePop
在 MainNav
小部件中创建的 Navigator
。这是可能的,因为传递给 SubNav
小部件的 build
方法的 BuildContext
在内部 Navigator
之外,因此在 [=23] 内部调用 Navigator.of(context)
=] 方法 returns 在 MainNav
小部件中创建的外部 Navigator
的 NavigatorState
。
我正在尝试在 Flutter Material 移动应用程序中实现新的声明式 Navigation
样式。我希望将多个页面堆叠在一起,并能够通过 Scaffolds 后退按钮一个接一个地弹出页面。
示例:一个应用有一个列出早餐食材的主屏幕,一个所有食材的详细信息页面,最后是一个可以从详细信息页面打开的页面,其中列出了喜欢该特定食材的人。只关注导航部分,这是设置:
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!")),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill")),
], onPopPage: (route, result) => route.didPop(result));
}
查看最低工作版本(状态管理和为简单起见而省略的所有其他内容):https://dartpad.dev/3d3451aba0f97016ae8c4b86a8b132bb
导航弹出按预期工作:应用程序从 pages
列表中定义的最后一页开始,通过单击 Scaffold
后退按钮,页面可以一个接一个地弹出,最多顶级页面。到目前为止一切顺利。
现在我想组织小部件,以便导航到“喜欢...的人”子页面不再是 MainNav
的责任,而是成分详细信息页面的责任.我引入了一个中间 Navigation
将详细信息页面推送到页面堆栈,如果需要,还有“喜欢...的人”页面。喜欢:
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("sub"), child: SubNav())
], onPopPage: (route, result) => route.didPop(result));
}
class SubNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!")),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill"))
],
onPopPage: (route, result) => route.didPop(result));
}
查看现场版:https://dartpad.dev/d0c38e2ec443aafc30d9e9f82ef158d9
这里发生的事情是从列表中弹出最后一页后,我们在“脆皮培根”页面上,不可能弹出这一页并返回到“早餐配料”页面列表。我可能遗漏了一些关于导航上下文的基本知识,这不是实现分层、声明式导航的方法。
所以问题是:如何仅使用声明性导航概念将导航任务委托到小部件层次结构中?有没有什么方法可以创建嵌套的 Navigation
小部件并让它们的所有 pages
表现为一个页面层次结构?
下面是一个最小工作示例,显示了您可以用来从内部 SubNav
小部件导航到外部 MainNav
小部件的方法之一:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nested declarative navigator',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainNav(),
);
}
}
class MainNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("one"), child: AppShell(title: "Breakfast ingredients", content: "Egg, Crispy bacon, beans")),
MaterialPage(key: ValueKey("sub"), child: SubNav())
], onPopPage: (route, result) => route.didPop(result));
}
class SubNav extends StatelessWidget {
@override
Widget build(BuildContext context) => Navigator(pages: [
MaterialPage(key: ValueKey("two"), child: AppShell(title: "Crispy bacon", content: "It's crispy!", leading: BackButton(onPressed: () => Navigator.of(context).maybePop()))),
MaterialPage(key: ValueKey("three"), child: AppShell(title: "People who like Crispy bacon", content: "John, Jill"))
],
onPopPage: (route, result) => route.didPop(result));
}
class AppShell extends StatelessWidget {
final String title;
final String content;
final Widget leading;
const AppShell({Key key, this.title, this.content, this.leading}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
leading: leading
),
body: Center(child: Text(content)));
}
}
我在 AppShell
小部件的构造函数中添加了一个可选的 leading
参数。这允许我们传入一个 BackButton
小部件,我们可以在 SubNav
小部件中创建自己的小部件。 BackButton
小部件有一个 onPressed
参数,我们可以使用它来 maybePop
在 MainNav
小部件中创建的 Navigator
。这是可能的,因为传递给 SubNav
小部件的 build
方法的 BuildContext
在内部 Navigator
之外,因此在 [=23] 内部调用 Navigator.of(context)
=] 方法 returns 在 MainNav
小部件中创建的外部 Navigator
的 NavigatorState
。