基于栈的导航
学习如何在 Flutter 应用中从一个页面导航到另一个页面。
学习使用 Navigator.push 在屏幕之间导航,并为不同屏幕尺寸实现自适应导航模式。
你将完成的内容
步骤
1
简介
简介
既然你已理解 sliver 与滚动,就可以实现屏幕之间的导航。在本课中,你将更新小屏视图,使得点击联系人分组时,会导航到该分组的联系人列表。
首先,还原自适应布局 widget 中的更改,使其在小屏上默认显示 ContactGroupsPage。
class _AdaptiveLayoutState extends State<AdaptiveLayout> {
int selectedListId = 0;
void _onContactListSelected(int listId) {
setState(() {
selectedListId = listId;
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLargeScreen = constraints.maxWidth > largeScreenMinWidth;
if (isLargeScreen) {
return _buildLargeScreenLayout();
} else {
return const ContactGroupsPage(); // Reverted
}
},
);
}
// ···
}
2
为联系人分组添加导航
为联系人分组添加导航
ContactGroupsPage 已使用 _ContactGroupsView
并为其提供了回调。需要更新该回调,使得点击分组时进行导航,而不是将分组打印到控制台。
确保 lib/screens/contact_groups.dart 中的
onListSelected 回调和 import 实现如下:
import 'contacts.dart';
class ContactGroupsPage extends StatelessWidget {
const ContactGroupsPage({super.key});
@override
Widget build(BuildContext context) {
return _ContactGroupsView(
onListSelected: (list) => Navigator.of(context).push(
CupertinoPageRoute<void>(
title: list.title,
builder: (context) => ContactListsPage(listId: list.id),
),
),
);
}
}
这一小段代码包含本页最重要的新信息。
Navigator.of(context) 从 widget 树中获取最近的 Navigator widget。
push 方法向 Navigator 的栈添加新路由,并显示 builder 属性返回的 widget。
这是使用基于栈的导航的最基本实现,新屏幕被 push 到当前屏幕之上。要返回上一屏幕,可使用 Navigator.pop 方法。
CupertinoPageRoute 创建 iOS 风格的页面转场,具有以下特性:
从右侧滑入的动画。
自动支持返回按钮。
正确的标题处理。
支持滑动返回手势。
3
为大屏创建侧边栏组件
为大屏创建侧边栏组件
对于大屏,你需要一个不进行导航、而是更新主内容区域的侧边栏。得益于上一步的重构,创建该组件更加直接。将此 widget 添加到 lib/screens/contact_groups.dart 底部:
/// A sidebar component for selecting contact groups on large screens.
class ContactGroupsSidebar extends StatelessWidget {
const ContactGroupsSidebar({
super.key,
required this.selectedListId,
required this.onListSelected,
});
final int selectedListId;
final void Function(int) onListSelected;
@override
Widget build(BuildContext context) {
return _ContactGroupsView(
selectedListId: selectedListId,
onListSelected: (list) => onListSelected(list.id),
);
}
}
该侧边栏组件复用 _ContactGroupsView 并提供不同的回调。它不进行导航,而是用被点击列表的 ID 调用 onListSelected。它还将 selectedListId 传给 _ContactGroupsView,以便高亮显示选中项。
4
为大屏创建详情视图
为大屏创建详情视图
对于大屏布局,你需要一个不显示导航控件的详情视图。与侧边栏一样,可以通过复用 _ContactListView 来重新创建。将此 widget 添加到你的 contacts.dart 文件底部:
class ContactListDetail extends StatelessWidget {
const ContactListDetail({super.key, required this.listId});
final int listId;
@override
Widget build(BuildContext context) {
return _ContactListView(listId: listId, automaticallyImplyLeading: false);
}
}
详情视图复用 _ContactListView,并将
automaticallyImplyLeading 参数设为 false 以隐藏返回按钮,因为导航由侧边栏处理。
5
将侧边栏连接到自适应布局
将侧边栏连接到自适应布局
现在,将侧边栏连接到你的自适应布局。更新 adaptive_layout.dart 文件以导入必要文件并更新大屏布局:
import 'package:flutter/cupertino.dart';
import 'contact_groups.dart';
import 'contacts.dart';
然后更新 _buildLargeScreenLayout 方法:
Widget _buildLargeScreenLayout() {
return CupertinoPageScaffold(
backgroundColor: CupertinoColors.extraLightBackgroundGray,
child: SafeArea(
child: Row(
children: [
SizedBox(
width: 320,
child: ContactGroupsSidebar(
selectedListId: selectedListId,
onListSelected: _onContactListSelected,
),
),
Container(width: 1, color: CupertinoColors.separator),
Expanded(child: ContactListDetail(listId: selectedListId)),
],
),
),
);
}
这段代码创建经典的菜单-详情布局,侧边栏控制详情区域的内容。
6
测试自适应导航行为
测试自适应导航行为
热重载应用并测试导航:
小屏(宽度 < 600px):
点击联系人分组以导航到联系人详情。
使用返回按钮或滑动手势返回。
这是经典的基于栈的导航流程。
大屏(宽度 > 600px):
在侧边栏中点击联系人分组以更新详情视图。
没有导航栈。选择会更新内容区域。
这是主从 (master-detail) 界面模式。
应用会根据屏幕尺寸自动选择合适的导航模式。这在手机和平板上都能提供最佳体验。
7
回顾
回顾
你已完成的内容
以下是你本课构建与学习内容的摘要。使用 Navigator.push 在屏幕之间导航
Navigator.of(context).push 向导航栈添加新路由。这是基于栈的导航的基础,屏幕彼此叠加上去,通过 pop 返回。
使用 CupertinoPageRoute 实现 iOS 风格转场
CupertinoPageRoute 提供原生 iOS 导航特性支持:从右侧滑入的动画、自动返回按钮、正确的标题处理,以及滑动返回手势支持。
实现了自适应导航模式
你为小屏和大屏设置了不同的导航模式。小屏使用基于栈的导航,点击分组会 push 新屏幕。大屏使用主从模式,选择分组会更新详情面板而无需导航。
完成了 Rolodex 应用
你已构建完整的 iOS 风格联系人应用,具备自适应布局、高级滚动、带搜索的可折叠标题和响应式导航。这些都是生产应用中常用的模式!
8
自测
自测
导航测验
1 / 2Navigator.of(context).push 的作用是什么?
-
用新屏幕替换当前屏幕。
不正确。
push 会添加到栈;
pushReplacement才会替换当前屏幕。 -
向导航栈添加新路由,显示在当前屏幕之上。
正确!
push 将新路由添加到栈,使用户可以返回上一屏幕。
-
从导航栈移除当前屏幕。
不正确。
那是
pop的作用;push 会添加新屏幕。 -
在当前屏幕上方打开对话框。
不正确。
对话框使用
showDialog;Navigator.push 用于导航到全屏界面。
Navigator.of(context).pop() 的作用是什么?
-
关闭整个应用。
不正确。
pop 仅移除当前路由;不会关闭应用。
-
从导航栈移除当前路由,返回上一屏幕。
正确!
pop 从栈顶移除路由,露出其下的屏幕。
-
清除所有路由并显示主屏幕。
不正确。
那需要 popUntil 或 pushAndRemoveUntil;pop 仅移除栈顶路由。
-
用新数据刷新当前屏幕。
不正确。
pop 用于返回导航;要刷新需使用 setState 或其他状态管理。
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-18。查看文档源码 或者 为本页面内容提出建议。