Skip to content

Scaffold

Scaffold 是 Material 应用的页面骨架,提供了 AppBar、Drawer、BottomNavigationBar、FAB 等槽位。

构造函数

Scaffold

dart
Scaffold({
  Key? key,                                                        // 组件标识
  PreferredSizeWidget? appBar,                                     // 顶部应用栏
  Widget? body,                                                    // 页面主体内容
  Widget? floatingActionButton,                                   // 浮动操作按钮
  FloatingActionButtonLocation? floatingActionButtonLocation,     // FAB 位置
  FloatingActionButtonAnimator? floatingActionButtonAnimator,     // FAB 动画
  List<Widget>? persistentFooterButtons,                           // 底部固定按钮组
  Widget? drawer,                                                  // 左侧抽屉菜单
  DrawerCallback? onDrawerChanged,                                // 抽屉开关回调
  Widget? endDrawer,                                              // 右侧抽屉菜单
  DrawerCallback? onEndDrawerChanged,                             // 右侧抽屉开关回调
  Widget? bottomNavigationBar,                                    // 底部导航栏
  Widget? bottomSheet,                                            // 底部持久化 Sheet
  Color? backgroundColor,                                          // 背景色
  bool? resizeToAvoidBottomInset,                                  // 是否避开键盘(默认 true)
  bool primary = true,                                            // 是否将 body 延伸到状态栏
  DragStartBehavior drawerDragStartBehavior = DragStartBehavior.start, // 抽屉拖拽行为
  double? drawerEdgeDragWidth,                                    // 抽屉边缘拖拽区域宽度
  bool drawerEnableOpenDragGesture = true,                        // 左抽屉是否可拖拽打开
  bool endDrawerEnableOpenDragGesture = true,                     // 右抽屉是否可拖拽打开
  String? restorationId,                                          // 状态恢复 ID
})

AppBar

dart
AppBar({
  Key? key,                                                        // 组件标识
  Widget? leading,                                                 // 左侧组件(通常为返回按钮)
  bool automaticallyImplyLeading = true,                          // 是否自动添加 leading(有 Drawer 时自动加菜单图标)
  Widget? title,                                                   // 标题组件
  List<Widget>? actions,                                           // 右侧操作按钮组
  Widget? flexibleSpace,                                           // 弹性空间(可实现渐变 AppBar)
  PreferredSizeWidget? bottom,                                     // 底部组件(通常为 TabBar)
  double? elevation,                                              // 阴影高度
  Color? shadowColor,                                            // 阴影颜色
  ShapeBorder? shape,                                             // 形状(圆角等)
  Color? backgroundColor,                                        // 背景色
  Color? foregroundColor,                                        // 前景色(标题、图标颜色)
  IconThemeData? iconTheme,                                      // 图标主题
  double? iconThemeData,                                          // 图标大小
  TextStyle? toolbarTextStyle,                                   // 工具栏文字样式
  TextStyle? titleTextStyle,                                     // 标题文字样式
  bool centerTitle,                                               // 标题是否居中(iOS 默认 true,Android 默认 false)
  double titleSpacing = NavigationToolbar.kMiddleSpacing,        // 标题间距
  double toolbarHeight = kToolbarHeight,                         // 工具栏高度(默认 56)
  double? leadingWidth,                                          // leading 区域宽度
  EdgeInsetsGeometry? toolbarOpacityCause,                       // 透明度
  bool primary = true,                                           // 是否留出状态栏空间
})

Drawer

dart
Drawer({
  Key? key,                                                        // 组件标识
  double? elevation,                                              // 阴影高度(默认 16)
  Widget? child,                                                   // 抽屉内容
  Color? backgroundColor,                                        // 背景色
  String? semanticLabel,                                          // 语义标签
})

属性速查

Scaffold 属性速查

属性类型默认值说明
appBarPreferredSizeWidget?null顶部应用栏
bodyWidget?null页面主体
floatingActionButtonWidget?null浮动操作按钮
floatingActionButtonLocationFloatingActionButtonLocation?nullFAB 位置
drawerWidget?null左侧抽屉
endDrawerWidget?null右侧抽屉
bottomNavigationBarWidget?null底部导航栏
bottomSheetWidget?null底部持久 Sheet
backgroundColorColor?白色背景色
resizeToAvoidBottomInsetbool?true是否避开键盘

AppBar 属性速查

属性类型默认值说明
leadingWidget?自动左侧组件(有 Drawer 时为菜单图标,有路由时为返回箭头)
titleWidget?null标题
actionsList<Widget>?null右侧操作按钮列表
bottomPreferredSizeWidget?null底部组件(TabBar)
elevationdouble?4阴影高度,0 为无阴影
backgroundColorColor?主题色背景色
centerTitlebool平台相关标题是否居中
toolbarHeightdouble56工具栏高度
flexibleSpaceWidget?null弹性空间(渐变背景等)

FAB 位置速查

位置
FloatingActionButtonLocation.endFloat右下角(默认)
FloatingActionButtonLocation.centerFloat底部居中
FloatingActionButtonLocation.startFloat左下角
FloatingActionButtonLocation.endDocked右下角嵌入 BottomAppBar
FloatingActionButtonLocation.centerDocked底部居中嵌入 BottomAppBar

快速示例

基本页面结构

dart
import 'package:flutter/material.dart';

class ScaffoldBasicExample extends StatelessWidget {
  const ScaffoldBasicExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('页面标题'),
        leading: IconButton(
          icon: Icon(Icons.menu),
          onPressed: () {},
        ),
        // ─── ★ 右侧操作按钮 ──────────────
        actions: [
          IconButton(icon: Icon(Icons.search), onPressed: () {}),
          IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
        ],
        // ─── ☆ 右侧操作按钮 ──────────────
      ),
      body: Center(child: Text('内容区')),
      // ─── ★ FAB ──────────────
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: Icon(Icons.add),
      ),
      // ─── ☆ FAB ──────────────
      // ─── ★ 底部导航 ──────────────
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
        ],
      ),
      // ─── ☆ 底部导航 ──────────────
    );
  }
}

带 Drawer 的页面

dart
import 'package:flutter/material.dart';

class ScaffoldDrawerExample extends StatelessWidget {
  const ScaffoldDrawerExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('带抽屉的页面')),
      // ─── ★ Drawer ──────────────
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            DrawerHeader(
          decoration: BoxDecoration(color: Colors.blue),
          child: Text('用户名', style: TextStyle(color: Colors.white, fontSize: 24)),
        ),
        ListTile(leading: Icon(Icons.home), title: Text('首页'), onTap: () {}),
        ListTile(leading: Icon(Icons.settings), title: Text('设置'), onTap: () {}),
        Divider(),
        ListTile(leading: Icon(Icons.logout), title: Text('退出'), onTap: () {}),
      ],
    ),
  ),
      // ─── ☆ Drawer ──────────────
  body: Center(child: Text('内容')),
)

带 TabBar 的 AppBar

dart
DefaultTabController(
  length: 3,
  child: Scaffold(
    appBar: AppBar(
      title: Text('Tab 示例'),
      bottom: TabBar(
        tabs: [
          Tab(icon: Icon(Icons.phone), text: '电话'),
          Tab(icon: Icon(Icons.favorite), text: '收藏'),
          Tab(icon: Icon(Icons.person), text: '通讯录'),
        ],
      ),
    ),
    body: TabBarView(
      children: [
        Center(child: Text('电话')),
        Center(child: Text('收藏')),
        Center(child: Text('通讯录')),
      ],
    ),
  ),
)

渐变 AppBar

dart
AppBar(
  title: Text('渐变 AppBar'),
  flexibleSpace: Container(
    decoration: BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.blue, Colors.purple],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
    ),
  ),
)

手动控制 Drawer

dart
// 获取 ScaffoldState 控制 Drawer
final scaffoldKey = GlobalKey<ScaffoldState>();

Scaffold(
  key: scaffoldKey,
  appBar: AppBar(
    title: Text('手动控制'),
    leading: IconButton(
      icon: Icon(Icons.menu),
      onPressed: () => scaffoldKey.currentState!.openDrawer(),
    ),
  ),
  drawer: Drawer(...),
)

// 关闭抽屉
Navigator.pop(context);

ScaffoldMessenger

ScaffoldMessenger 是 SnackBar 的管理器,负责在 Scaffold 底部显示/隐藏 SnackBar。它解决了旧 API Scaffold.of(context).showSnackBar() 的 context 问题。

为什么用 ScaffoldMessenger 而不是 Scaffold.of

方式问题
Scaffold.of(context).showSnackBar()body 内部的 context 找不到外层 Scaffold,需要 Builder 包裹
ScaffoldMessenger.of(context).showSnackBar()✅ 直接使用,无需 Builder,推荐

ScaffoldMessengerState 方法速查

方法说明
showSnackBar(SnackBar)显示 SnackBar
hideCurrentSnackBar([reason])隐藏当前 SnackBar
removeCurrentSnackBar([reason])移除当前 SnackBar(无动画)
clearSnackBars()清空所有排队的 SnackBar

SnackBarClosedReason 速查

说明
SnackBarClosedReason.action用户点击了 action 按钮
SnackBarClosedReason.dismiss用户手动关闭(滑掉或点击外部)
SnackBarClosedReason.hide调用 hideCurrentSnackBar
SnackBarClosedReason.remove调用 removeCurrentSnackBar
SnackBarClosedReason.timeout显示时长结束自动关闭

全局配置 ScaffoldMessenger

dart
// 在 MaterialApp 中配置,所有页面共享同一个 ScaffoldMessenger
MaterialApp(
  scaffoldMessengerKey: scaffoldMessengerKey,     // 全局 key
  home: HomePage(),
)

// 在任何地方使用
final scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();

scaffoldMessengerKey.currentState?.showSnackBar(
  SnackBar(content: Text('全局通知')),
);

ScaffoldMessenger 示例

dart
// 1. 基本 SnackBar
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text('操作成功')),
);

// 2. 带操作按钮
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    content: Text('已删除 1 项'),
    duration: Duration(seconds: 5),
    action: SnackBarAction(
      label: '撤销',
      onPressed: () {
        // 撤销操作
      },
    ),
  ),
);

// 3. 浮动样式
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    content: Text('保存成功'),
    behavior: SnackBarBehavior.floating,
    margin: EdgeInsets.fromLTRB(16, 0, 16, 16),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  ),
);

// 4. 监听 SnackBar 关闭原因
final scaffoldMessenger = ScaffoldMessenger.of(context);
final snackBar = SnackBar(content: Text('可撤销'));
scaffoldMessenger.showSnackBar(snackBar).closed.then((reason) {
  if (reason == SnackBarClosedReason.action) {
    print('用户点击了操作按钮');
  } else if (reason == SnackBarClosedReason.timeout) {
    print('自动超时关闭');
  }
});

// 5. 避免排队:先清除再显示
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text('最新消息')),
);

// 6. 自定义 SnackBar(带进度条)
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(
    duration: Duration(seconds: 10),
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text('上传中...'),
        SizedBox(height: 8),
        LinearProgressIndicator(),
      ],
    ),
  ),
);

常见错误

1. Scaffold 内嵌 Scaffold

dart
// ❌ 错误:Scaffold 内嵌 Scaffold 会导致多个 AppBar 和布局混乱
Scaffold(
  body: Scaffold(           // 不要嵌套!
    appBar: AppBar(...),
    body: ...,
  ),
)

// ✅ 修复:外层用 Scaffold,内层只用内容
Scaffold(
  appBar: AppBar(...),
  body: YourContent(),
)

2. 键盘弹出时布局溢出

dart
// ✅ 修复:设置 resizeToAvoidBottomInset
Scaffold(
  resizeToAvoidBottomInset: true,   // 默认 true,确保未设为 false
  body: SingleChildScrollView(       // 内容可滚动
    child: Column(children: [...]),
  ),
)

3. body 中使用 SnackBar 的 context 问题

dart
// ❌ 旧方式:body 中调用 Scaffold.of(context).showSnackBar() 会报错
Scaffold(
  body: Builder(
    builder: (context) {
      Scaffold.of(context).showSnackBar(...);  // 可能报错
    },
  ),
)

// ✅ 推荐方式:使用 ScaffoldMessenger
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text('操作成功')),
);

基于 Flutter 官方文档整理