Selection
Flutter 提供多种选择控件:Checkbox(复选框)、Radio(单选框)、Switch(开关)、Slider(滑块)。
构造函数
Checkbox
dart
Checkbox({
Key? key, // 组件标识
required bool? value, // 选中状态(必填,null 为三态)
bool tristate = false, // 是否启用三态(false → true → null)
required ValueChanged<bool?>? onChanged, // 状态变化回调(必填)
MouseCursor? mouseCursor, // 鼠标指针
Color? activeColor, // 选中颜色
Color? fillColor, // 填充颜色
Color? checkColor, // 勾选标记颜色
Color? focusColor, // 焦点颜色
Color? hoverColor, // 悬停颜色
Color? overlayColor, // 覆盖颜色
double splashRadius, // 水波纹半径
MaterialTapTargetSize? materialTapTargetSize, // 触摸目标大小
VisualDensity? visualDensity, // 视觉密度
FocusNode? focusNode, // 焦点节点
bool autofocus = false, // 自动焦点
OutlinedBorder? shape, // 形状
BorderSide? side, // 边框
})CheckboxListTile
dart
CheckboxListTile({ // 带标题和副标题的复选框
Key? key,
required bool? value, // 选中状态
required ValueChanged<bool?>? onChanged, // 变化回调
Color? activeColor, // 选中颜色
Widget? title, // 标题
Widget? subtitle, // 副标题
bool isThreeLine = false, // 是否三行高度
bool dense = false, // 是否紧凑
bool secondary, // 右侧控件
bool selected = false, // 是否选中(影响文字颜色)
ListTileControlAffinity controlAffinity = ListTileControlAffinity.platform, // 控件位置
bool autofocus = false,
EdgeInsetsGeometry? contentPadding, // 内边距
})Radio
dart
Radio<T>({
Key? key,
required T value, // 当前选项值(必填)
required T? groupValue, // 当前选中值(必填)
required ValueChanged<T?>? onChanged, // 变化回调(必填)
Color? activeColor, // 选中颜色
Color? fillColor, // 填充颜色
Color? focusColor, // 焦点颜色
Color? hoverColor, // 悬停颜色
Color? overlayColor, // 覆盖颜色
double splashRadius, // 水波纹半径
MaterialTapTargetSize? materialTapTargetSize,
VisualDensity? visualDensity,
FocusNode? focusNode,
bool autofocus = false,
MouseCursor? mouseCursor,
bool toggleable = false, // 是否可取消选中
})RadioListTile
dart
RadioListTile<T>({ // 带标题的单选项
Key? key,
required T value,
required T? groupValue,
required ValueChanged<T?>? onChanged,
Color? activeColor,
Widget? title,
Widget? subtitle,
bool isThreeLine = false,
bool dense = false,
Widget? secondary,
bool selected = false,
ListTileControlAffinity controlAffinity = ListTileControlAffinity.platform,
bool autofocus = false,
EdgeInsetsGeometry? contentPadding,
})Switch
dart
Switch({
Key? key,
required bool value, // 开关状态(必填)
required ValueChanged<bool> onChanged, // 变化回调(必填)
Color? activeColor, // 开启颜色(滑块轨道)
Color? activeTrackColor, // 开启轨道颜色
Color? inactiveThumbColor, // 关闭滑块颜色
Color? inactiveTrackColor, // 关闭轨道颜色
Color? thumbColor, // 滑块颜色
Color? trackColor, // 轨道颜色
ImageProvider? activeThumbImage, // 开启滑块图片
ImageProvider? inactiveThumbImage, // 关闭滑块图片
MaterialTapTargetSize? materialTapTargetSize,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
Color? focusColor,
Color? hoverColor,
FocusNode? focusNode,
bool autofocus = false,
})SwitchListTile
dart
SwitchListTile({ // 带标题的开关
Key? key,
required bool value,
required ValueChanged<bool> onChanged,
Color? activeColor,
Widget? title,
Widget? subtitle,
bool isThreeLine = false,
bool dense = false,
Widget? secondary,
bool selected = false,
ListTileControlAffinity controlAffinity = ListTileControlAffinity.platform,
bool autofocus = false,
EdgeInsetsGeometry? contentPadding,
})Slider
dart
Slider({
Key? key,
required double value, // 当前值(必填)
required double min = 0.0, // 最小值
required double max = 1.0, // 最大值
required ValueChanged<double>? onChanged, // 值变化回调(必填)
ValueChanged<double>? onChangeStart, // 开始拖动回调
ValueChanged<double>? onChangeEnd, // 结束拖动回调
int? divisions, // 离散分段数(null 为连续)
String? label, // 拖动时显示的标签
Color? activeColor, // 激活颜色
Color? inactiveColor, // 非激活颜色
Color? thumbColor, // 滑块颜色
MouseCursor? mouseCursor,
SemanticFormatterCallback? semanticFormatterCallback,
FocusNode? focusNode,
bool autofocus = false,
})RangeSlider
dart
RangeSlider({ // 范围滑块
Key? key,
required RangeValues values, // 当前范围值(必填)
required double min = 0.0, // 最小值
required double max = 1.0, // 最大值
required ValueChanged<RangeValues>? onChanged, // 值变化回调(必填)
ValueChanged<RangeValues>? onChangeStart,
ValueChanged<RangeValues>? onChangeEnd,
int? divisions,
RangeLabels? labels, // 起止标签
Color? activeColor,
Color? inactiveColor,
MouseCursor? mouseCursor,
SemanticFormatterCallback? semanticFormatterCallback,
FocusNode? focusNode,
bool autofocus = false,
})属性速查
选择控件对比
| 控件 | 用途 | 状态类型 | 典型场景 |
|---|---|---|---|
Checkbox | 多选 | bool? | 同意条款、多标签选择 |
Radio | 单选 | T (泛型) | 性别、支付方式 |
Switch | 开关 | bool | 设置开关、通知开关 |
Slider | 范围值 | double | 音量、亮度、价格范围 |
Checkbox 属性速查
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | bool? | 必填 | 选中状态,null 为三态 |
onChanged | ValueChanged<bool?>? | 必填 | 状态变化回调 |
tristate | bool | false | 三态模式:false → true → null |
activeColor | Color? | 主题色 | 选中颜色 |
checkColor | Color? | 白色 | 勾选标记颜色 |
fillColor | MaterialStateProperty<Color?>? | null | 填充颜色(可按状态) |
Radio 属性速查
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | T | 必填 | 当前选项的值 |
groupValue | T? | 必填 | 当前选中的值(同一组共享) |
onChanged | ValueChanged<T?>? | 必填 | 选中回调 |
activeColor | Color? | 主题色 | 选中颜色 |
toggleable | bool | false | 是否可取消选中 |
Switch 属性速查
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | bool | 必填 | 开关状态 |
onChanged | ValueChanged<bool> | 必填 | 状态变化回调 |
activeColor | Color? | 主题色 | 开启时轨道颜色 |
activeTrackColor | Color? | null | 开启时轨道颜色 |
inactiveThumbColor | Color? | null | 关闭时滑块颜色 |
inactiveTrackColor | Color? | null | 关闭时轨道颜色 |
Slider 属性速查
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | double | 必填 | 当前值 |
min | double | 0.0 | 最小值 |
max | double | 1.0 | 最大值 |
onChanged | ValueChanged<double>? | 必填 | 值变化回调 |
divisions | int? | null | 离散分段数,null 为连续 |
label | String? | null | 拖动时标签 |
activeColor | Color? | 主题色 | 已滑过部分颜色 |
inactiveColor | Color? | 灰色 | 未滑过部分颜色 |
ListTile 控件位置速查
controlAffinity 值 | 说明 |
|---|---|
ListTileControlAffinity.leading | 控件在左侧(默认) |
ListTileControlAffinity.trailing | 控件在右侧 |
ListTileControlAffinity.platform | 跟随平台 |
快速示例
Checkbox
dart
bool isChecked = false;
Checkbox(
value: isChecked,
onChanged: (value) {
setState(() => isChecked = value!);
},
activeColor: Colors.blue,
)
// 带 ListTile
CheckboxListTile(
value: isChecked,
onChanged: (value) => setState(() => isChecked = value!),
title: Text('同意用户协议'),
subtitle: Text('请阅读并同意'),
controlAffinity: ListTileControlAffinity.leading, // 复选框在左
)Radio
dart
enum Gender { male, female }
Gender? selectedGender;
Column(
children: [
Radio<Gender>(
value: Gender.male,
groupValue: selectedGender,
onChanged: (value) => setState(() => selectedGender = value),
),
Radio<Gender>(
value: Gender.female,
groupValue: selectedGender,
onChanged: (value) => setState(() => selectedGender = value),
),
],
)
// 带 ListTile
RadioListTile<Gender>(
value: Gender.male,
groupValue: selectedGender,
onChanged: (value) => setState(() => selectedGender = value),
title: Text('男'),
)Switch
dart
bool notificationsEnabled = true;
Switch(
value: notificationsEnabled,
onChanged: (value) => setState(() => notificationsEnabled = value),
activeColor: Colors.green,
)
// 带 ListTile
SwitchListTile(
value: notificationsEnabled,
onChanged: (value) => setState(() => notificationsEnabled = value),
title: Text('推送通知'),
subtitle: Text('接收新消息通知'),
)Slider
dart
double volume = 50;
Slider(
value: volume,
min: 0,
max: 100,
divisions: 10, // 离散 10 档
label: volume.round().toString(), // 拖动时显示标签
onChanged: (value) => setState(() => volume = value),
)
// 范围滑块
RangeValues priceRange = RangeValues(100, 500);
RangeSlider(
values: priceRange,
min: 0,
max: 1000,
divisions: 20,
labels: RangeLabels('¥${priceRange.start.round()}', '¥${priceRange.end.round()}'),
onChanged: (values) => setState(() => priceRange = values),
)完整设置页面示例
dart
class SettingsPage extends StatefulWidget {
@override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
bool darkMode = false;
bool notifications = true;
double fontSize = 16;
String language = 'zh';
@override
Widget build(BuildContext context) {
return ListView(
children: [
SwitchListTile(
title: Text('深色模式'),
value: darkMode,
onChanged: (v) => setState(() => darkMode = v),
),
SwitchListTile(
title: Text('通知'),
subtitle: Text('接收推送通知'),
value: notifications,
onChanged: (v) => setState(() => notifications = v),
),
ListTile(
title: Text('字体大小: ${fontSize.round()}'),
subtitle: Slider(
value: fontSize,
min: 12,
max: 24,
divisions: 6,
label: fontSize.round().toString(),
onChanged: (v) => setState(() => fontSize = v),
),
),
RadioListTile<String>(
title: Text('中文'),
value: 'zh',
groupValue: language,
onChanged: (v) => setState(() => language = v!),
),
RadioListTile<String>(
title: Text('English'),
value: 'en',
groupValue: language,
onChanged: (v) => setState(() => language = v!),
),
],
);
}
}常见错误
1. Radio 的 groupValue 未共享
dart
// ❌ 错误:每个 Radio 用不同的变量
Radio(value: 1, groupValue: selected1, onChanged: ...),
Radio(value: 2, groupValue: selected2, onChanged: ...), // 不同的变量!
// ✅ 修复:同一组 Radio 共享同一个 groupValue
Radio(value: 1, groupValue: selectedGender, onChanged: ...),
Radio(value: 2, groupValue: selectedGender, onChanged: ...),2. onChanged 为 null 导致无法交互
dart
// ❌ 错误:条件禁用时直接传 null
Checkbox(
value: isChecked,
onChanged: isEnabled ? null : (v) {}, // 逻辑反了!
)
// ✅ 修复:isEnabled 为 true 时传回调,false 时传 null
Checkbox(
value: isChecked,
onChanged: isEnabled ? (v) => setState(() => isChecked = v!) : null,
)