在前面的章节中,我们已经掌握了单个页面的几乎所有构建要素:布局、列表、表单、弹窗。现在的挑战是:如何把这些独立的页面串联起来,构建一个完整的 App?
第四部分将聚焦于:
- 页面跳转与管理 (Navigator):核心的栈(Stack)管理机制,推入新页面与返回上一页。
- 参数传递 (Data Passing):如何在页面跳转时发送数据(如:点击商品列表 -> 进入商品详情页)。
- 整体导航结构 (App Structure):如何实现底部导航栏 (
BottomNavigationBar)、顶部选项卡 (TabBar) 和侧边菜单 (Drawer)。
第四部分:多页面管理与导航 (Navigation & Routing)
第七章:导航与路由 (Navigation)
Flutter 的导航管理采用“栈”(Stack)的概念:
- Push (推入):将一个新页面盖在当前页面之上。
- Pop (弹出):将当前页面移出,露出下面的页面(即返回)。
1. 基础跳转 (Navigator.push & pop)
说明:
这是最直接的跳转方式,不需要预先注册路由表,适合大多数场景。
核心方法:
Navigator.of(context).push(Route route): 跳转。Navigator.of(context).pop(): 返回。MaterialPageRoute: Material 设计风格的路由(Android 是向上/淡入动画,iOS 是侧滑动画)。
代码示例 (页面 A 跳转到 页面 B):
import 'package:flutter/material.dart';
// 页面 A (首页)
class FirstPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("第一页")),
body: Center(
child: ElevatedButton(
child: Text("去第二页 ->"),
onPressed: () {
// 执行跳转
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
),
),
);
}
}
// 页面 B (详情页)
class SecondPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("第二页")),
body: Center(
child: ElevatedButton(
child: Text("<- 返回"),
onPressed: () {
// 执行返回
Navigator.of(context).pop();
},
),
),
);
}
}
2. 页面间传值 (Passing Data)
场景: 点击一个列表项,进入详情页,并把该项的 ID 或标题传过去。
方法: 通过目标页面的构造函数传递参数。
代码示例:
// 1. 定义数据模型
class Product {
final String name;
final double price;
Product(this.name, this.price);
}
// 2. 列表页
class ProductListScreen extends StatelessWidget {
final List<Product> products = [
Product("iPhone 15", 5999),
Product("MacBook Pro", 12999),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("商品列表")),
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].name),
onTap: () {
// 跳转并传值
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailScreen(product: products[index]),
),
);
},
);
},
),
);
}
}
// 3. 详情页 (接收参数)
class ProductDetailScreen extends StatelessWidget {
// 定义接收的字段
final Product product;
// 构造函数要求必传 product
const ProductDetailScreen({Key? key, required this.product}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(product.name)),
body: Center(
child: Text("价格: ¥${product.price}", style: TextStyle(fontSize: 24)),
),
);
}
}
进阶:从目标页面返回数据
有时我们需要从详情页带回数据(例如:在“选择地址页”选好后返回“订单确认页”)。
// 在页面 A 中
final result = await Navigator.push(context, MaterialPageRoute(...));
print("页面 B 返回了: $result");
// 在页面 B 中
Navigator.pop(context, "我是返回的数据");
3. 底部导航栏 (BottomNavigationBar)
说明:
这是 App 最常见的一级导航结构(如微信底部的:微信、通讯录、发现、我)。
实现原理是:使用 Scaffold 的 bottomNavigationBar 属性,配合 IndexedStack 或简单的 List<Widget> 来切换 body 的内容。
核心属性:
currentIndex: 当前激活项的索引 (int)。onTap: 点击回调,用于更新索引。items: 导航项列表 (BottomNavigationBarItem)。type:BottomNavigationBarType.fixed(图标均分) 或shifting(点击变大)。
代码示例 (标准 App 骨架):
import 'package:flutter/material.dart';
class MainAppBase extends StatefulWidget {
_MainAppBaseState createState() => _MainAppBaseState();
}
class _MainAppBaseState extends State<MainAppBase> {
int _currentIndex = 0;
// 页面列表
final List<Widget> _pages = [
Center(child: Text("首页内容", style: TextStyle(fontSize: 30))),
Center(child: Text("消息中心", style: TextStyle(fontSize: 30))),
Center(child: Text("个人中心", style: TextStyle(fontSize: 30))),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("My App")),
// 动态切换 Body
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed, // 超过3个item建议设为fixed
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon: Icon(Icons.message), label: "消息"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "我的"),
],
),
);
}
}
4. 顶部选项卡 (TabBar & TabBarView)
说明:
用于在同一页面内切换不同的视图(如 WhatsApp 顶部的 Calls, Chats, Contacts)。通常配合 AppBar 的 bottom 属性使用。
实现方式:
最简单的方法是使用 DefaultTabController 包裹整个 Scaffold,无需手动管理 Controller。
代码示例:
import 'package:flutter/material.dart';
class TabBarExample extends StatelessWidget {
Widget build(BuildContext context) {
// length: 3 表示有三个标签
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text("Top Tabs"),
// TabBar 放在 AppBar 的 bottom 位置
bottom: TabBar(
indicatorColor: Colors.white, // 指示器颜色
tabs: [
Tab(icon: Icon(Icons.directions_car), text: "汽车"),
Tab(icon: Icon(Icons.directions_transit), text: "地铁"),
Tab(icon: Icon(Icons.directions_bike), text: "骑行"),
],
),
),
// TabBarView 的 children 数量必须与 tabs 一致
body: TabBarView(
children: [
Center(child: Text("汽车页面")),
Center(child: Text("地铁页面")),
Center(child: Text("骑行页面")),
],
),
),
);
}
}
5. 侧边抽屉导航 (Drawer)
说明:
点击左上角汉堡菜单滑出的侧边栏。
注意:
在 Drawer 中点击跳转时,通常需要先关闭 Drawer (Navigator.pop(context)),否则返回时 Drawer 依然是打开状态。
代码示例:
Drawer(
child: ListView(
padding: EdgeInsets.zero, // 移除顶部 padding,让 Header 顶满屏幕
children: [
UserAccountsDrawerHeader(
accountName: Text("Flutter User"),
accountEmail: Text("user@example.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Text("F", style: TextStyle(fontSize: 40.0)),
),
decoration: BoxDecoration(color: Colors.blue),
),
ListTile(
leading: Icon(Icons.settings),
title: Text("设置"),
onTap: () {
// 1. 关闭侧边栏
Navigator.pop(context);
// 2. 跳转到设置页 (假设有 SettingsPage)
// Navigator.push(context, MaterialPageRoute(builder: (c) => SettingsPage()));
},
),
],
),
)
附录:路由管理的常见问题
-
Named Routes (命名路由):
- 在大型项目中,可能会看到
Navigator.pushNamed(context, '/detail')这种写法。 - 这需要在
MaterialApp的routes属性中预先定义好映射关系。 - 建议: 初学者先熟练使用
MaterialPageRoute(匿名路由),因为它传参更直观,且有类型检查。
- 在大型项目中,可能会看到
-
黑屏问题:
- 如果跳转后页面一片漆黑,通常是因为新页面没有使用
Scaffold作为根节点。记住:每个全屏页面最好都以Scaffold开始。
- 如果跳转后页面一片漆黑,通常是因为新页面没有使用
-
Context 问题:
Navigator.of(context)需要的 context 必须是含在MaterialApp之下的。如果在main()函数里直接用,会报错。
第四部分总结:
恭喜你!到目前为止,你已经掌握了:
- UI 构建:Text, Image, Layouts, Styling.
- 数据展示:ListView, GridView.
- 用户交互:Inputs, Forms, Dialogs.
- 页面流转:Navigator, BottomNavigationBar, Tabs.
这四部分内容已经构成了一个完整的静态 App 开发体系。你可以做出来的 App 看起来已经和真实上线的产品没什么两样了。
最后一步:赋予灵魂。
现在的 App 还是“死”的,数据是写死的,逻辑是同步的。
第五部分 将是本系列的终章,涵盖 功能性组件与异步编程。
我们将学习:
- Theme (主题):一键切换夜间模式/主题色。
- GestureDetector (手势):让任意组件都可点击、拖拽。
- FutureBuilder / StreamBuilder:最重要的一环,如何处理网络请求的加载中、加载失败、加载完成状态。
1069

被折叠的 条评论
为什么被折叠?



