前言
在 Flutter 开发中,Scaffold 是你创建 Material 风格页面的"一站式骨架"。
一个标准页面里常见的 AppBar、Drawer、BottomNavigationBar、Snackbar、FloatingActionButton......其实都是 Scaffold 统一管理。
然而多数文章只是"属性罗列",并没有告诉你 真实业务里该怎么用、如何组件化、如何搭配状态管理、如何做可维护页面架构。
这篇文章会告诉你:
-
为什么 Scaffold 是 Flutter 页面框架的核心
-
每个属性到底什么时候用
-
实战项目架构应该怎么组织
-
常见坑 + 实战解决方案
-
适合发布的完整示例代码 + 最佳实践
让你写页面不再堆属性,而是写出结构清晰、可扩展、可维护的 Flutter 页面。
1. 什么是 Scaffold?为什么它这么关键?
一句话总结:
Scaffold 是"页面外壳",你只负责把内容塞进去。
Android 对应什么?
-
类似 Activity + Toolbar + DrawerLayout + BottomNavigationView 的组合
-
Flutter 把这些统一交由 Scaffold 管理
如果你在做企业级 App、管理后台、机器人控制端(如我在项目中遇到的),Scaffold 的价值更高:
它能确保所有页面结构统一、交互一致、逻辑可扩展。
2. Scaffold 的核心结构(脑图式理解)
Scaffold
├── appBar(顶部区域:标题、菜单、返回)
├── body(页面主体,最核心)
├── floatingActionButton(悬浮主操作按钮)
├── bottomNavigationBar(底部导航)
├── drawer / endDrawer(左右侧抽屉)
├── bottomSheet(固定底部操作区)
├── snackbar / materialBanner(提示)
换成一句话就是:
你只写业务内容(body),其他交给 Scaffold。
3. Scaffold 常用属性深度解析(不是罗列,是告诉你"何时用")
appBar -- 顶部导航栏
适合放:
- 页面标题
- 搜索按钮
- 更多菜单(PopupMenu)
- 状态/模式切换按钮(例如:机器人模式切换)
示例:
Dart
appBar: AppBar(
title: const Text('设备监控'),
actions: [
IconButton(icon: Icon(Icons.refresh), onPressed: _reload),
IconButton(icon: Icon(Icons.settings), onPressed: _openSettings),
],
)
最佳实践
- 若整个 App 都使用统一标题风格,建议抽一层
AppBarBuilder。 - 若包含动态状态(连接中/断开),标题区可以结合 StreamBuilder/BlocBuilder。
body -- 页面核心内容
这里写真正的页面内容。
业务上,body 常见的三种模式:
① 普通内容页
body: Center(child: Text('Hello Flutter')),
② 多 Tab 切换(推荐 IndexedStack)
保持页面状态不重建:
Dart
body: IndexedStack(
index: _index,
children: _pages,
)
③ 实时数据(机器人/监控系统常用)
Dart
body: StreamBuilder<RobotStatus>(
stream: robotStatusStream,
builder: (_, snap) {
if (!snap.hasData) return CircularProgressIndicator();
return StatusPanel(data: snap.data!);
},
)
最佳实践
-
不要把复杂逻辑写在 Scaffold 里,应放 ViewModel/BLoC。
-
body 尽量拆组件,提高可维护性。
floatingActionButton -- 主操作按钮(FAB)
适合:
- 添加内容
- 关键操作(如"紧急停止")
- 快速动作入口
最佳实践
如果你的页面会有多个 FAB 功能,建议使用 SpeedDial 扩展库。
bottomNavigationBar -- 底部导航
适合:
- 多页面主导航(首页 / 监控 / 设置)
典型写法:
Dart
bottomNavigationBar: BottomNavigationBar(
currentIndex: _index,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.monitor), label: '监控'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
],
onTap: _changeIndex,
),
最佳实践
- 配合 IndexedStack,避免页面切换重建
- 如果页面较多(>5),建议使用 NavigationRail(特别是平板/机器人屏幕)
drawer -- 左侧抽屉菜单
适合:
- 设置中心
- 日志系统
- 账号切换
- 低频功能入口
Dart
drawer: Drawer(
child: ListView(
children: const [
DrawerHeader(child: Text("系统菜单")),
ListTile(title: Text("系统设置")),
ListTile(title: Text("日志中心")),
],
),
),
最佳实践
在你的机器人系统里,这里可以放:
-
模块运行状态
-
版本信息
-
网络配置
-
传感器状态
4. 实战:构建一个真正可复用的 Scaffold 模板(企业级/App 大型项目推荐)
多数公司项目不会直接写 Scaffold,而是封装:
Dart
BaseScaffold
├── 内置统一 AppBar 样式
├── 包装 Drawer
├── 包装 FAB 行为约束
├── 包装 ScaffoldMessenger
├── 包装 Loading / Error UI
一个非常干净的可复用模板:
Dart
class BasePage extends StatelessWidget {
final String title;
final Widget body;
final List<Widget>? actions;
const BasePage({
required this.title,
required this.body,
this.actions,
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title), actions: actions),
body: SafeArea(child: body),
floatingActionButton: _buildFab(),
drawer: const AppDrawer(),
);
}
Widget? _buildFab() {
return FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
);
}
}
以后业务页面这样写:
Dart
return BasePage(
title: "机器人监控",
body: MonitorView(),
actions: [IconButton(icon: Icon(Icons.refresh), onPressed: _reload)],
);
页面干净、结构统一、可复用性极高。
5. Scaffold 常见坑(重点)
| 问题 | 原因 | 解决办法 |
|---|---|---|
| 键盘弹出导致页面跳动 | resizeToAvoidBottomInset 默认开启 | 设置为 false |
| FAB 被键盘挡住 | Scaffold 不会自动处理 | 配合 FloatingActionButtonLocation.centerDocked或自定义 |
| BottomNavigationBar 切换重建 | 页面重新 build | 用 IndexedStack |
| NestedScrollView + TabBar + AppBar 出现滑动 Bug | 默认滚动行为冲突 | 使用 SliverAppBar + TabBarView |
6. 完整示例:一个真实业务风格的页面
可以直接复制运行👇:
Dart
class MonitorPage extends StatefulWidget {
const MonitorPage({super.key});
@override
State<MonitorPage> createState() => _MonitorPageState();
}
class _MonitorPageState extends State<MonitorPage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("机器人实时监控"),
actions: [
IconButton(icon: Icon(Icons.refresh), onPressed: () {}),
],
),
drawer: const AppDrawer(),
body: IndexedStack(
index: _index,
children: const [
HomeView(),
MonitorView(),
SettingsView(),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.stop),
backgroundColor: Colors.red,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _index,
onTap: (i) => setState(() => _index = i),
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),
BottomNavigationBarItem(icon: Icon(Icons.monitor), label: "监控"),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),
],
),
);
}
}
总结
在 Flutter 中,Scaffold 不只是"一个页面容器",而是页面结构的框架核心。
用好 Scaffold,你就能:
✔ 让页面结构统一
✔ 让导航逻辑清晰
✔ 让功能扩展更容易
✔ 让复杂应用(如机器人控制台 / 监控系统)保持可维护性
新手会堆属性,但高手会构建页面框架。Scaffold,就是这个框架的起点。