Button
Flutter 提供了多种按钮组件,适用于不同场景。Material 3 风格下主要有四种按钮:
| 按钮 | 用途 | 特点 |
|---|---|---|
ElevatedButton | 主要操作 | 有阴影/高度感,视觉最突出 |
FilledButton | 主要操作(M3 推荐) | 纯色填充,无阴影 |
OutlinedButton | 次要操作 | 有边框,无填充背景 |
TextButton | 低优先级操作 | 无边框无填充,最轻量 |
IconButton | 图标操作 | 仅图标,无文字 |
构造函数
ElevatedButton
dart
ElevatedButton({
Key? key, // 组件标识
required VoidCallback? onPressed, // 点击回调(必填,null 则禁用)
VoidCallback? onLongPress, // 长按回调
ValueChanged<bool>? onHover, // 悬停状态变化回调
ValueChanged<bool>? onFocusChange, // 焦点状态变化回调
ButtonStyle? style, // 按钮样式
FocusNode? focusNode, // 焦点控制节点
bool autofocus = false, // 是否自动获取焦点
Clip clipBehavior = Clip.none, // 裁剪行为
MaterialStatesController? statesController, // 状态控制器
Widget? child, // 子组件(通常为 Text)
})
ElevatedButton.icon({ // 带图标的构造函数
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool autofocus = false,
Clip clipBehavior = Clip.none,
MaterialStatesController? statesController,
required Widget icon, // 图标(必填)
required Widget label, // 文字标签(必填)
})FilledButton
dart
FilledButton({ // M3 推荐的主要按钮
Key? key, // 组件标识
required VoidCallback? onPressed, // 点击回调(必填)
VoidCallback? onLongPress, // 长按回调
ValueChanged<bool>? onHover, // 悬停回调
ValueChanged<bool>? onFocusChange, // 焦点回调
ButtonStyle? style, // 按钮样式
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
Clip clipBehavior = Clip.none, // 裁剪行为
MaterialStatesController? statesController, // 状态控制器
Widget? child, // 子组件
})
FilledButton.icon({ // 带图标版本
...同上...
required Widget icon, // 图标(必填)
required Widget label, // 文字标签(必填)
})OutlinedButton
dart
OutlinedButton({ // 边框按钮
Key? key,
required VoidCallback? onPressed, // 点击回调(必填)
VoidCallback? onLongPress, // 长按回调
ValueChanged<bool>? onHover, // 悬停回调
ValueChanged<bool>? onFocusChange, // 焦点回调
ButtonStyle? style, // 按钮样式
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
Clip clipBehavior = Clip.none, // 裁剪行为
MaterialStatesController? statesController, // 状态控制器
Widget? child, // 子组件
})
OutlinedButton.icon({
...同上...
required Widget icon,
required Widget label,
})TextButton
dart
TextButton({ // 文字按钮
Key? key,
required VoidCallback? onPressed, // 点击回调(必填)
VoidCallback? onLongPress, // 长按回调
ValueChanged<bool>? onHover, // 悬停回调
ValueChanged<bool>? onFocusChange, // 焦点回调
ButtonStyle? style, // 按钮样式
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
Clip clipBehavior = Clip.none, // 裁剪行为
MaterialStatesController? statesController, // 状态控制器
Widget? child, // 子组件
})
TextButton.icon({
...同上...
required Widget icon,
required Widget label,
})IconButton
dart
IconButton({ // 图标按钮
Key? key, // 组件标识
double? iconSize, // 图标大小(默认 24)
VisualDensity? visualDensity, // 视觉密度
EdgeInsetsGeometry padding = const EdgeInsets.all(8), // 内边距
AlignmentGeometry alignment = Alignment.center, // 图标对齐方式
bool enableFeedback = true, // 是否启用触觉反馈
double? splashRadius, // 水波纹半径
Color? color, // 图标颜色
Color? focusColor, // 焦点颜色
Color? hoverColor, // 悬停颜色
Color? highlightColor, // 高亮颜色
Color? splashColor, // 水波纹颜色
Color? disabledColor, // 禁用颜色
MouseCursor? mouseCursor, // 鼠标指针样式
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
String? tooltip, // 长按提示文字
bool? isSelected, // 是否选中(用于切换按钮)
ValueChanged<bool>? onPressed, // 选中状态变化回调
required Widget icon, // 图标组件(必填)
})Floating Action Button (FAB)
dart
FloatingActionButton({ // 浮动操作按钮
Key? key,
Widget? child, // 子组件(通常为 Icon)
String? tooltip, // 长按提示文字
Color? foregroundColor, // 前景色(图标颜色)
Color? backgroundColor, // 背景色
Color? focusColor, // 焦点颜色
Color? hoverColor, // 悬停颜色
Color? splashColor, // 水波纹颜色
Object? heroTag, // Hero 动画标签
double? elevation, // 阴影高度
double? focusElevation, // 焦点阴影高度
double? hoverElevation, // 悬停阴影高度
double? highlightElevation, // 高亮阴影高度
double? disabledElevation, // 禁用阴影高度
required VoidCallback? onPressed, // 点击回调(必填)
ShapeBorder shape = const CircleBorder(), // 形状
bool mini = false, // 是否为迷你尺寸
Clip clipBehavior = Clip.none, // 裁剪行为
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
MaterialTapTargetSize? materialTapTargetSize, // 触摸目标大小
bool isExtended = false, // 是否为扩展模式
})
FloatingActionButton.extended({ // 带 label 的扩展 FAB
Key? key,
required Widget icon, // 图标(必填)
required Widget label, // 文字标签(必填)
String? tooltip,
Color? foregroundColor,
Color? backgroundColor,
Color? focusColor,
Color? hoverColor,
Color? splashColor,
Object? heroTag,
double? elevation,
double? focusElevation,
double? hoverElevation,
required VoidCallback? onPressed,
ShapeBorder shape = const StadiumBorder(),
Clip clipBehavior = Clip.none,
FocusNode? focusNode,
bool autofocus = false,
MaterialTapTargetSize? materialTapTargetSize,
})属性速查
通用属性(ElevatedButton / FilledButton / OutlinedButton / TextButton 共有)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
onPressed | VoidCallback? | 必填 | 点击回调,null 时按钮禁用 |
onLongPress | VoidCallback? | null | 长按回调 |
onHover | ValueChanged<bool>? | null | 悬停状态变化回调 |
onFocusChange | ValueChanged<bool>? | null | 焦点状态变化回调 |
style | ButtonStyle? | null | 按钮样式(颜色、形状、大小等) |
focusNode | FocusNode? | null | 焦点控制节点 |
autofocus | bool | false | 是否自动获取焦点 |
clipBehavior | Clip | Clip.none | 裁剪行为 |
child | Widget? | 必填 | 子组件(通常为 Text) |
IconButton 专属属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
icon | Widget | 必填 | 图标组件 |
iconSize | double? | 24 | 图标大小 |
color | Color? | 主题色 | 图标颜色 |
padding | EdgeInsetsGeometry | EdgeInsets.all(8) | 内边距 |
alignment | AlignmentGeometry | Alignment.center | 图标对齐方式 |
tooltip | String? | null | 长按提示文字 |
splashRadius | double? | null | 水波纹半径 |
isSelected | bool? | null | 是否选中(切换按钮) |
FloatingActionButton 专属属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
child | Widget? | 必填 | 子组件(通常为 Icon) |
backgroundColor | Color? | 主题色 | 背景色 |
foregroundColor | Color? | 白色 | 前景色(图标颜色) |
elevation | double? | 6 | 阴影高度 |
shape | ShapeBorder | CircleBorder() | 形状 |
heroTag | Object? | 默认自动 | Hero 动画标签(同一页面多个 FAB 需设不同值) |
mini | bool | false | 是否为迷你尺寸(40x40) |
isExtended | bool | false | 是否为扩展模式(带文字) |
ButtonStyle 速查
ButtonStyle 是按钮自定义的核心,通过 MaterialStateProperty 实现不同状态下的样式。
dart
ButtonStyle({
MaterialStateProperty<TextStyle?>? textStyle, // 文字样式
MaterialStateProperty<Color?>? backgroundColor, // 背景色
MaterialStateProperty<Color?>? foregroundColor, // 前景色(文字/图标)
MaterialStateProperty<Color?>? overlayColor, // 覆盖色(水波纹/高亮)
MaterialStateProperty<double?>? elevation, // 阴影高度
MaterialStateProperty<Color?>? shadowColor, // 阴影颜色
MaterialStateProperty<double?>? padding, // 内边距
MaterialStateProperty<Size?>? minimumSize, // 最小尺寸
MaterialStateProperty<Size?>? fixedSize, // 固定尺寸
MaterialStateProperty<Size?>? maximumSize, // 最大尺寸
MaterialStateProperty<BorderSide?>? side, // 边框
MaterialStateProperty<OutlinedBorder?>? shape, // 形状(圆角等)
MaterialStateProperty<MouseCursor?>? mouseCursor, // 鼠标指针
MaterialStateProperty<VisualDensity?>? visualDensity, // 视觉密度
MaterialTapTargetSize? tapTargetSize, // 触摸目标大小
MaterialStateProperty<AlignmentGeometry?>? alignment, // 子组件对齐
MaterialStateProperty<EdgeInsetsGeometry?>? iconSize, // 图标间距
})ButtonStyle 快捷设置方式
dart
// 方式1:使用 styleFrom 快捷方法(推荐)
ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // 背景色
foregroundColor: Colors.white, // 文字/图标颜色
elevation: 4, // 阴影高度
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), // 内边距
shape: RoundedRectangleBorder( // 形状
borderRadius: BorderRadius.circular(8),
),
textStyle: TextStyle(fontSize: 16), // 文字样式
side: BorderSide(color: Colors.blue, width: 2), // 边框(OutlinedButton 常用)
)
// 方式2:直接构造 ButtonStyle(适合按状态区分样式)
ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.disabled)) return Colors.grey;
if (states.contains(MaterialState.pressed)) return Colors.blue[700];
return Colors.blue;
}),
)styleFrom 方法速查
| 方法 | 适用按钮 | 说明 |
|---|---|---|
ElevatedButton.styleFrom() | ElevatedButton | 有阴影的按钮样式 |
FilledButton.styleFrom() | FilledButton | 纯色填充样式 |
OutlinedButton.styleFrom() | OutlinedButton | 边框按钮样式 |
TextButton.styleFrom() | TextButton | 文字按钮样式 |
styleFrom 常用参数
| 参数 | 类型 | 说明 |
|---|---|---|
backgroundColor | Color? | 背景色 |
foregroundColor | Color? | 前景色(文字、图标、水波纹) |
disabledBackgroundColor | Color? | 禁用状态背景色 |
disabledForegroundColor | Color? | 禁用状态前景色 |
elevation | double? | 阴影高度 |
padding | EdgeInsetsGeometry? | 内边距 |
shape | OutlinedBorder? | 形状 |
side | BorderSide? | 边框 |
textStyle | TextStyle? | 文字样式 |
minimumSize | Size? | 最小尺寸 |
fixedSize | Size? | 固定尺寸 |
maximumSize | Size? | 最大尺寸 |
shadowColor | Color? | 阴影颜色 |
visualDensity | VisualDensity? | 视觉密度(紧凑/标准/舒适) |
按钮类型对比速查
| 特性 | ElevatedButton | FilledButton | OutlinedButton | TextButton | IconButton |
|---|---|---|---|---|---|
| 背景 | 有(带阴影) | 有(纯色填充) | 无 | 无 | 无 |
| 边框 | 无 | 无 | 有 | 无 | 无 |
| 视觉权重 | 高 | 高 | 中 | 低 | 低 |
| 典型场景 | CTA/确认 | CTA/确认 | 次要操作 | 对话框/工具栏 | 工具栏/卡片 |
| M3 推荐 | - | ✅ 主要操作 | ✅ 次要操作 | ✅ 低优先级 | ✅ 图标操作 |
快速示例
以下示例都需要在文件顶部添加
import 'package:flutter/material.dart';
基本按钮
dart
import 'package:flutter/material.dart';
class ButtonTypesExample extends StatelessWidget {
const ButtonTypesExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('按钮类型')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 主要操作按钮
// ─── ★ 各按钮类型 ──────────────
ElevatedButton(
onPressed: () {},
child: const Text('Elevated'),
),
// ─── ☆ 各按钮类型 ──────────────
const SizedBox(height: 12),
// M3 推荐的主要按钮
FilledButton(
onPressed: () {},
child: const Text('Filled'),
),
const SizedBox(height: 12),
// 次要操作按钮
OutlinedButton(
onPressed: () {},
child: const Text('Outlined'),
),
const SizedBox(height: 12),
// 文字按钮
TextButton(
onPressed: () {},
child: const Text('Text'),
),
],
),
),
);
}
}带图标的按钮
dart
import 'package:flutter/material.dart';
class IconButtonExample extends StatelessWidget {
const IconButtonExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('带图标按钮')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 方式1:使用 .icon 构造函数
ElevatedButton.icon(
onPressed: () {},
icon: const Icon(Icons.add),
label: const Text('添加'),
),
const SizedBox(height: 12),
// 图标按钮
IconButton(
onPressed: () {},
icon: const Icon(Icons.favorite),
color: Colors.red,
tooltip: '收藏',
iconSize: 28,
),
],
),
),
);
}
}浮动操作按钮 (FAB)
dart
import 'package:flutter/material.dart';
class FabExample extends StatelessWidget {
const FabExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('FAB 示例')),
body: const Center(child: Text('FAB 示例')),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
}自定义样式
dart
import 'package:flutter/material.dart';
class CustomStyleButtonExample extends StatelessWidget {
const CustomStyleButtonExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义按钮样式')),
body: Center(
child: ElevatedButton(
onPressed: () {},
// ─── ★ styleFrom 自定义样式 ──────────────
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
elevation: 8,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
textStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
// ─── ☆ styleFrom 自定义样式 ──────────────
child: const Text('自定义按钮'),
),
),
);
}
}禁用状态
dart
import 'package:flutter/material.dart';
class DisabledButtonExample extends StatefulWidget {
const DisabledButtonExample({super.key});
@override
State<DisabledButtonExample> createState() => _DisabledButtonExampleState();
}
class _DisabledButtonExampleState extends State<DisabledButtonExample> {
bool _isEnabled = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('按钮禁用状态')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
// ─── ★ null 禁用按钮 ──────────────
onPressed: _isEnabled ? () => print('提交') : null,
// ─── ☆ null 禁用按钮 ──────────────
child: const Text('提交'),
),
const SizedBox(height: 16),
SwitchListTile(
title: const Text('启用按钮'),
value: _isEnabled,
onChanged: (value) => setState(() => _isEnabled = value),
),
],
),
),
);
}
}常见错误
1. 按钮无限宽度
dart
// ❌ 错误:按钮默认会撑满可用宽度(在 Row 中不会,在 Column 中可能)
Column(
children: [
ElevatedButton(...), // 可能撑满宽度
],
)
// ✅ 修复:使用 Align 或 SizedBox 限制宽度
Column(
children: [
SizedBox(
width: double.infinity,
child: ElevatedButton(...),
),
],
)2. Row 中多个按钮溢出
dart
// ❌ 错误:Row 中多个按钮未用 Expanded 包裹
Row(
children: [
ElevatedButton(...),
ElevatedButton(...), // 可能溢出
],
)
// ✅ 修复:使用 Expanded
Row(
children: [
Expanded(child: ElevatedButton(...)),
SizedBox(width: 16),
Expanded(child: ElevatedButton(...)),
],
)3. 同一页面多个 FAB 的 heroTag 冲突
dart
// ❌ 错误:同一页面多个 FAB 未设置 heroTag,导航时会报错
FloatingActionButton(onPressed: () {}, child: Icon(Icons.add)),
FloatingActionButton(onPressed: () {}, child: Icon(Icons.edit)),
// ✅ 修复:为每个 FAB 设置不同的 heroTag
FloatingActionButton(
heroTag: 'add',
onPressed: () {},
child: Icon(Icons.add),
),
FloatingActionButton(
heroTag: 'edit',
onPressed: () {},
child: Icon(Icons.edit),
),