Skip to content

Material 3 按钮样式规范

最后更新:2026-01-18 版本:v1.0 状态:✅ 强制执行

💡 提示:本文档是按钮的详细实战指南。如需查看完整的 Material 3 设计系统规范(包括文本、图标、颜色等基础规范),请参考 Material 3 设计系统


📋 目录

  1. 按钮类型选择
  2. 样式规范
  3. 布局规范
  4. 代码示例
  5. 常见错误

按钮类型选择

决策树

按钮用途?
├── 主要操作(保存、确认、提交)
│   └── ✅ FilledButton(primary背景)

├── 次要操作(跳过、稍后、下一步)
│   └── ✅ FilledButton.tonal(secondaryContainer背景)

├── 取消/返回操作
│   └── ✅ OutlinedButton(透明背景+边框)

└── 低优先级操作(链接、内联操作)
    └── ✅ TextButton(无背景无边框)

按钮类型对照表

按钮类型Material 3 组件背景色使用场景示例
主按钮FilledButtonprimary主要操作(保存、确认、提交)"保存书籍"、"添加到生词本"
次按钮FilledButton.tonalsecondaryContainer次要操作(跳过、下一步)"跳过"、"稍后提醒"
边框按钮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('保存'),
)

颜色映射表

按钮类型背景色文字色自动推导
FilledButtonprimaryonPrimary✅ 是
FilledButton.tonalsecondaryContaineronSecondaryContainer✅ 是
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