主题
Material 3 按钮样式规范
最后更新:2026-01-18 版本:v1.0 状态:✅ 强制执行
💡 提示:本文档是按钮的详细实战指南。如需查看完整的 Material 3 设计系统规范(包括文本、图标、颜色等基础规范),请参考 Material 3 设计系统
📋 目录
按钮类型选择
决策树
按钮用途?
├── 主要操作(保存、确认、提交)
│ └── ✅ FilledButton(primary背景)
│
├── 次要操作(跳过、稍后、下一步)
│ └── ✅ FilledButton.tonal(secondaryContainer背景)
│
├── 取消/返回操作
│ └── ✅ OutlinedButton(透明背景+边框)
│
└── 低优先级操作(链接、内联操作)
└── ✅ TextButton(无背景无边框)按钮类型对照表
| 按钮类型 | Material 3 组件 | 背景色 | 使用场景 | 示例 |
|---|---|---|---|---|
| 主按钮 | FilledButton | primary | 主要操作(保存、确认、提交) | "保存书籍"、"添加到生词本" |
| 次按钮 | FilledButton.tonal | secondaryContainer | 次要操作(跳过、下一步) | "跳过"、"稍后提醒" |
| 边框按钮 | OutlinedButton | 透明 + 边框 | 取消、返回、重置 | "取消"、"重新调整" |
| 文本按钮 | TextButton | 透明 | 低优先级、对话框操作 | "了解更多"、"关闭" |
样式规范
1. 圆角规范
统一使用 12px 圆角
dart
// ✅ 正确
FilledButton(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('确认'),
)
// ❌ 错误(圆角不一致)
FilledButton(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 8px 圆角
),
),
child: const Text('确认'),
)2. 高度规范
| 场景 | 高度 | 使用方式 |
|---|---|---|
| 标准按钮 | 48px | 默认(无需设置) |
| 大型按钮 | 56px | 底部导航、主CTA |
| 紧凑按钮 | 40px | 卡片内、工具栏 |
dart
// ✅ 标准高度(默认48px,无需设置)
FilledButton(
child: const Text('确认'),
)
// ✅ 大型按钮(显式设置)
SizedBox(
height: 56,
child: FilledButton(
child: const Text('添加到生词本'),
),
)3. 颜色规范
❌ 禁止硬编码颜色
dart
// ❌ 错误:硬编码白色
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white, // ❌ 硬编码!深色主题会出错
),
child: const Text('保存'),
)✅ 使用 Material 3 语义化颜色
dart
// ✅ 正确:自动推导颜色
FilledButton(
// backgroundColor: primary(自动)
// foregroundColor: onPrimary(自动)
child: const Text('保存'),
)颜色映射表
| 按钮类型 | 背景色 | 文字色 | 自动推导 |
|---|---|---|---|
FilledButton | primary | onPrimary | ✅ 是 |
FilledButton.tonal | secondaryContainer | onSecondaryContainer | ✅ 是 |
OutlinedButton | 透明 | primary | ✅ 是 |
TextButton | 透明 | primary | ✅ 是 |
4. 宽度规范
底部固定按钮:使用全宽
dart
// ✅ 底部保存/确认按钮
Container(
padding: const EdgeInsets.all(16),
child: SafeArea(
child: SizedBox(
width: double.infinity, // 全宽
height: 48,
child: FilledButton(
onPressed: _save,
child: const Text('保存'),
),
),
),
)浮动按钮:自适应内容
dart
// ✅ 浮动操作按钮
FilledButton(
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
onPressed: _confirm,
child: const Text('Add 5 to Notebook'), // 宽度自适应
)布局规范
1. 按钮组布局
单个按钮:居中或全宽
dart
// 对话框:居中
actions: [
FilledButton(
onPressed: _confirm,
child: const Text('确认'),
),
]
// 底部操作:全宽
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: _save,
child: const Text('保存'),
),
)两个按钮:取消+确认
dart
// ✅ 正确:取消在左,确认在右
Row(
children: [
Expanded(
child: OutlinedButton( // 取消(次要)
onPressed: _cancel,
child: const Text('取消'),
),
),
const SizedBox(width: 16), // 间距16px
Expanded(
flex: 2, // 主操作占更大宽度
child: FilledButton( // 确认(主要)
onPressed: _confirm,
child: const Text('确认'),
),
),
],
)对话框按钮:TextButton + FilledButton
dart
// ✅ 对话框标准布局
actions: [
TextButton( // 取消
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
FilledButton( // 确认
onPressed: _confirm,
child: const Text('保存'),
),
]2. 间距规范
| 场景 | 间距 |
|---|---|
| 按钮之间水平间距 | 16px |
| 按钮与内容垂直间距 | 16px |
| 按钮组内边距 | 16px |
代码示例
示例1:底部保存按钮(全宽)
dart
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
offset: const Offset(0, -2),
blurRadius: 4,
),
],
),
child: SafeArea(
child: SizedBox(
width: double.infinity,
height: 48,
child: FilledButton(
onPressed: _isLoading ? null : _save,
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: _isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.onPrimary,
),
),
)
: const Text(
'保存',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
),
)示例2:对话框按钮组
dart
AlertDialog(
title: const Text('确认操作'),
content: const Text('确定要删除这个项目吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
FilledButton(
onPressed: () {
_delete();
Navigator.pop(context);
},
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('删除'),
),
],
)示例3:底部操作组(取消+确认)
dart
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Theme.of(context).colorScheme.outline,
width: 1,
),
),
),
child: SafeArea(
child: Row(
children: [
// 取消按钮
Expanded(
child: OutlinedButton(
onPressed: _cancel,
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('取消'),
),
),
const SizedBox(width: 16),
// 确认按钮
Expanded(
flex: 2,
child: FilledButton(
onPressed: _confirm,
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('确认'),
),
),
],
),
),
)常见错误
❌ 错误1:使用废弃的 ElevatedButton
dart
// ❌ 错误
ElevatedButton(
onPressed: _save,
child: const Text('保存'),
)
// ✅ 正确
FilledButton(
onPressed: _save,
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('保存'),
)❌ 错误2:硬编码颜色
dart
// ❌ 错误
FilledButton(
style: FilledButton.styleFrom(
backgroundColor: Colors.blue, // 硬编码!
foregroundColor: Colors.white,
),
child: const Text('确认'),
)
// ✅ 正确
FilledButton(
// 自动使用 primary/onPrimary
child: const Text('确认'),
)❌ 错误3:次要操作用主按钮
dart
// ❌ 错误:"取消"用了主按钮
FilledButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
)
// ✅ 正确:次要操作用 tonal 或 outlined
FilledButton.tonal( // 或 OutlinedButton
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
)❌ 错误4:圆角不统一
dart
// ❌ 错误:使用了 8px 圆角
FilledButton(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 不统一!
),
),
child: const Text('确认'),
)
// ✅ 正确:统一使用 12px
FilledButton(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('确认'),
)快速参考
常用按钮样式模板
dart
// 主按钮模板
FilledButton(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: _onPressed,
child: const Text('按钮文字'),
)
// 次要按钮模板
FilledButton.tonal(
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: _onPressed,
child: const Text('按钮文字'),
)
// 边框按钮模板
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: _onPressed,
child: const Text('按钮文字'),
)相关文档
维护者:Reading Vocab Helper Team 反馈:发现问题请提Issue