主题
Material 3 导航组件样式规范
最后更新:2026-01-18 版本:v1.0 状态:✅ 强制执行
💡 提示:本文档是导航组件的详细实战指南。如需查看完整的 Material 3 设计系统规范(包括文本、图标、颜色等基础规范),请参考 Material 3 设计系统
📋 目录
组件类型选择
决策树
导航类型?
├── 顶部导航栏
│ ├── 标准页面(有返回) → ✅ AppBar + leading: BackButton
│ ├── 主页面(无返回) → ✅ AppBar + leading: null
│ └── 搜索页面 → ✅ AppBar + TextField
│
├── 底部导航(2-5个主导航)
│ └── ✅ BottomNavigationBar / NavigationBar (Material 3)
│
├── 侧边导航(5+个导航项)
│ ├── 移动端 → ✅ Drawer
│ └── 平板/桌面端 → ✅ NavigationRail
│
└── Tab导航(2-6个标签)
└── ✅ TabBar + TabBarView组件类型对照表
| 组件 | Material 3 组件 | 使用场景 | 示例 |
|---|---|---|---|
| 顶部导航 | AppBar | 页面标题、操作按钮、搜索 | 所有页面顶部 |
| 底部导航 | NavigationBar (M3) / BottomNavigationBar | 主导航(2-5项) | 首页、生词本、统计、设置 |
| 侧边导航 | Drawer | 辅助导航、设置、用户信息 | 侧滑菜单 |
| 导航栏 | NavigationRail | 平板/桌面侧边导航 | 大屏设备 |
| Tab导航 | TabBar | 页面内容切换 | 统计页面内的不同视图 |
AppBar 样式规范
1. 基础样式
Material 3 默认样式:
dart
// ✅ 标准 AppBar
AppBar(
title: Text('页面标题'),
// 自动样式:
// - 背景色:surface
// - 标题颜色:onSurface
// - 图标颜色:onSurfaceVariant
)2. 高度规范
| 场景 | 高度 |
|---|---|
| 标准 AppBar | 56dp |
| 带搜索的 AppBar | 56dp |
| 大标题 AppBar | 112dp (large) |
dart
// ✅ 标准高度(默认)
AppBar(
title: Text('标题'),
)
// ⚠️ 大标题(SliverAppBar)
SliverAppBar(
expandedHeight: 112,
title: Text('大标题'),
)3. 颜色规范
背景色(自动)
dart
// ✅ 正确:使用默认背景色
AppBar(
title: Text('标题'),
// 自动使用 colorScheme.surface
)
// ⚠️ 自定义背景色(特殊场景)
AppBar(
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
title: Text('标题'),
)
// ❌ 错误:硬编码颜色
AppBar(
backgroundColor: Colors.blue, // 不适配主题
title: Text('标题'),
)前景色(自动)
dart
// ✅ 正确:自动使用主题颜色
AppBar(
title: Text('标题'),
// 标题颜色:onSurface(自动)
// 图标颜色:onSurfaceVariant(自动)
)4. 标题样式
dart
// ✅ 默认标题样式
AppBar(
title: Text('标题'),
// 自动使用 titleLarge (22sp, w400)
)
// ⚠️ 自定义标题样式(需要时使用)
AppBar(
title: Text(
'标题',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
),
),
)5. 操作按钮
返回按钮
dart
// ✅ 自动显示返回按钮(有路由栈时)
AppBar(
title: Text('详情页'),
// leading 自动显示 BackButton
)
// ⚠️ 自定义返回按钮
AppBar(
title: Text('详情页'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
)
// ⚠️ 隐藏返回按钮
AppBar(
title: Text('主页'),
automaticallyImplyLeading: false,
)右侧操作按钮
dart
// ✅ 单个操作
AppBar(
title: Text('标题'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
],
)
// ✅ 多个操作(最多3个)
AppBar(
title: Text('标题'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
),
],
)
// ❌ 错误:操作按钮过多(> 3个)
AppBar(
title: Text('标题'),
actions: [
IconButton(...),
IconButton(...),
IconButton(...),
IconButton(...), // 太多了!
],
)6. 搜索 AppBar
dart
// ✅ 搜索页面 AppBar
AppBar(
title: TextField(
autofocus: true,
decoration: InputDecoration(
hintText: '搜索书籍...',
border: InputBorder.none,
hintStyle: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
actions: [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
// 清空搜索
},
),
],
)7. SliverAppBar(可折叠)
dart
// ✅ 可折叠 AppBar
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('可折叠标题'),
background: Image.asset(
'assets/header.jpg',
fit: BoxFit.cover,
),
),
),
SliverList(...),
],
)BottomNavigationBar 样式规范
1. Material 3 NavigationBar(推荐)
dart
// ✅ Material 3 NavigationBar
NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) {
setState(() => _currentIndex = index);
},
destinations: [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
NavigationDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: '生词本',
),
NavigationDestination(
icon: Icon(Icons.bar_chart_outlined),
selectedIcon: Icon(Icons.bar_chart),
label: '统计',
),
NavigationDestination(
icon: Icon(Icons.settings_outlined),
selectedIcon: Icon(Icons.settings),
label: '设置',
),
],
)2. BottomNavigationBar(兼容)
dart
// ⚠️ Material 2 BottomNavigationBar(兼容旧代码)
BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);
},
type: BottomNavigationBarType.fixed, // 固定模式
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Theme.of(context).colorScheme.onSurfaceVariant,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.book_outlined),
activeIcon: Icon(Icons.book),
label: '生词本',
),
BottomNavigationBarItem(
icon: Icon(Icons.bar_chart_outlined),
activeIcon: Icon(Icons.bar_chart),
label: '统计',
),
],
)3. 导航项数量规范
| 数量 | 推荐 | 说明 |
|---|---|---|
| 2个 | ⚠️ | 太少,考虑使用 TabBar |
| 3-5个 | ✅ | 理想数量 |
| 5+个 | ❌ | 太多,使用 Drawer 或 NavigationRail |
4. 图标规范
使用 Outlined + Filled 图标对
dart
// ✅ 正确:未选中用 Outlined,选中用 Filled
NavigationDestination(
icon: Icon(Icons.home_outlined), // 未选中
selectedIcon: Icon(Icons.home), // 选中
label: '首页',
)
// ❌ 错误:未区分选中状态
NavigationDestination(
icon: Icon(Icons.home), // 始终 Filled
label: '首页',
)NavigationRail 样式规范
1. 基础用法(平板/桌面)
dart
// ✅ NavigationRail(侧边导航栏)
Row(
children: [
NavigationRail(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() => _selectedIndex = index);
},
labelType: NavigationRailLabelType.all, // 显示所有标签
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: Text('首页'),
),
NavigationRailDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: Text('生词本'),
),
NavigationRailDestination(
icon: Icon(Icons.bar_chart_outlined),
selectedIcon: Icon(Icons.bar_chart),
label: Text('统计'),
),
],
),
VerticalDivider(thickness: 1, width: 1),
Expanded(
child: _pages[_selectedIndex],
),
],
)2. 标签显示模式
| 模式 | 说明 |
|---|---|
NavigationRailLabelType.none | 不显示标签 |
NavigationRailLabelType.selected | 仅选中项显示标签 |
NavigationRailLabelType.all | 所有项显示标签(推荐) |
Drawer 样式规范
1. 基础样式
dart
// ✅ Drawer 侧滑菜单
Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
// 头部
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
radius: 32,
child: Icon(Icons.person, size: 32),
),
SizedBox(height: 12),
Text(
'用户名',
style: Theme.of(context).textTheme.titleLarge,
),
Text(
'user@example.com',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
// 菜单项
ListTile(
leading: Icon(Icons.home),
title: Text('首页'),
onTap: () {
Navigator.pop(context);
// 导航逻辑
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('设置'),
onTap: () {
Navigator.pop(context);
// 导航逻辑
},
),
Divider(),
ListTile(
leading: Icon(Icons.logout),
title: Text('退出登录'),
onTap: () {
Navigator.pop(context);
// 登出逻辑
},
),
],
),
)2. DrawerHeader 样式
dart
// ✅ 使用 primaryContainer 背景色
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: ...,
)
// ❌ 错误:硬编码颜色
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue, // 不适配主题
),
child: ...,
)代码示例
示例1:完整主页框架(AppBar + BottomNavigationBar)
dart
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _pages = [
HomeTabPage(),
NotebookTabPage(),
StatisticsTabPage(),
SettingsTabPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_getTitle()),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
// 搜索
},
),
],
),
body: _pages[_currentIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) {
setState(() => _currentIndex = index);
},
destinations: [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
NavigationDestination(
icon: Icon(Icons.book_outlined),
selectedIcon: Icon(Icons.book),
label: '生词本',
),
NavigationDestination(
icon: Icon(Icons.bar_chart_outlined),
selectedIcon: Icon(Icons.bar_chart),
label: '统计',
),
NavigationDestination(
icon: Icon(Icons.settings_outlined),
selectedIcon: Icon(Icons.settings),
label: '设置',
),
],
),
);
}
String _getTitle() {
switch (_currentIndex) {
case 0:
return '首页';
case 1:
return '生词本';
case 2:
return '统计';
case 3:
return '设置';
default:
return '';
}
}
}示例2:带 Drawer 的页面
dart
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Reading Vocab Helper'),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
CircleAvatar(
radius: 32,
backgroundColor:
Theme.of(context).colorScheme.primary,
child: Icon(
Icons.person,
size: 32,
color: Theme.of(context).colorScheme.onPrimary,
),
),
SizedBox(height: 12),
Text(
'学习者',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
),
],
),
),
ListTile(
leading: Icon(Icons.home),
title: Text('首页'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.book),
title: Text('我的书籍'),
onTap: () {
Navigator.pop(context);
// 导航
},
),
ListTile(
leading: Icon(Icons.download),
title: Text('导入/导出'),
onTap: () {
Navigator.pop(context);
// 导航
},
),
Divider(),
ListTile(
leading: Icon(Icons.settings),
title: Text('设置'),
onTap: () {
Navigator.pop(context);
// 导航
},
),
ListTile(
leading: Icon(Icons.help_outline),
title: Text('帮助'),
onTap: () {
Navigator.pop(context);
// 导航
},
),
],
),
),
body: Center(
child: Text('主页内容'),
),
);
}
}示例3:TabBar 导航
dart
class StatisticsPage extends StatefulWidget {
@override
_StatisticsPageState createState() => _StatisticsPageState();
}
class _StatisticsPageState extends State<StatisticsPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('统计'),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: '总览'),
Tab(text: 'CEFR'),
Tab(text: '趋势'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
OverviewTab(),
CEFRTab(),
TrendTab(),
],
),
);
}
}常见错误
❌ 错误1:AppBar 硬编码颜色
dart
// ❌ 错误:硬编码背景色
AppBar(
backgroundColor: Colors.blue,
title: Text('标题'),
)
// ✅ 正确:使用默认颜色
AppBar(
title: Text('标题'),
// 自动使用 colorScheme.surface
)❌ 错误2:底部导航项过多
dart
// ❌ 错误:导航项太多(6个)
NavigationBar(
destinations: [
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...), // 太多了!
],
)
// ✅ 正确:3-5个导航项
NavigationBar(
destinations: [
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...),
NavigationDestination(...),
],
)❌ 错误3:图标未区分选中状态
dart
// ❌ 错误:未使用 selectedIcon
NavigationDestination(
icon: Icon(Icons.home),
label: '首页',
)
// ✅ 正确:使用 Outlined + Filled 图标对
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
)❌ 错误4:AppBar 操作按钮过多
dart
// ❌ 错误:操作按钮超过3个
AppBar(
title: Text('标题'),
actions: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
IconButton(icon: Icon(Icons.filter_list), onPressed: () {}),
IconButton(icon: Icon(Icons.sort), onPressed: () {}),
IconButton(icon: Icon(Icons.share), onPressed: () {}),
],
)
// ✅ 正确:最多3个,多余的放到菜单中
AppBar(
title: Text('标题'),
actions: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
IconButton(icon: Icon(Icons.filter_list), onPressed: () {}),
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(child: Text('排序')),
PopupMenuItem(child: Text('分享')),
],
),
],
)❌ 错误5:DrawerHeader 硬编码颜色
dart
// ❌ 错误:硬编码背景色
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: ...,
)
// ✅ 正确:使用主题颜色
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: ...,
)快速参考
常用导航组件模板
dart
// AppBar 模板
AppBar(
title: Text('标题'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
],
)
// NavigationBar 模板
NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: (index) {
setState(() => _currentIndex = index);
},
destinations: [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: '首页',
),
// ...更多导航项
],
)
// Drawer 模板
Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: Text('Header'),
),
ListTile(
leading: Icon(Icons.home),
title: Text('首页'),
onTap: () => Navigator.pop(context),
),
],
),
)相关文档
维护者:Reading Vocab Helper Team 反馈:发现问题请提Issue