曾无好事来相访,赖尔高文一起予。这篇文章主要讲述Flutter 专题109 图解自定义 ACERadio 单选框 #yyds干货盘点#相关的知识,希望能为你提供帮助。
Radio 单选框在日常应用中很常见,Flutter 提供的单选框与 Android 提供的略有不同,小菜简单了解一下并对其进行部分扩展;
文章图片
Radio Radio 单选框是在一组选项中,互斥的选择单个选项;
源码分析
class Radio<
T>
extends StatefulWidget
const Radio(
Key key,
@required this.value,// 当前单选框设置的值
@required this.groupValue,// 当前单选框选定状态的值
@required this.onChanged,// 选中回调
this.activeColor,// 选中状态颜色
this.focusColor,// 获取焦点时颜色
this.hoverColor,// 高亮时颜色
this.materialTapTargetSize, // 点击范围最小大小
this.focusNode,
this.autofocus = false,
)
简单分析源码可得,Radio 是一个有状态的 StatefulWidget 小组件;Radio 单选框本身不保持任何状态,通过 onChanged 回调,来判断当前 value 是否与 groupValue 选项组中对应的 item 是否一致,来判断选中状态;一般通过调用 State.setState() 更新单选按钮的 groupValue 从而响应 onChanged 回调;
案例尝试
onChanged Radio 单选框一般分为三个状态,分别为未选中状态、选中状态和不可选中状态;onChanged 为单选框选中的回调,根据 value 和 groupValue 匹配是否为选中状态;当 onChanged 为 null 时,单选框为不可选中状态;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
Radio(value: GenderType.MALE, groupValue: _groupValue,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = https://www.songbingjia.com/android/val);
),
Text(男)
]),
Row(children: <
Widget>
[
Radio(value: GenderType.FEMALE, groupValue: _groupValue,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = val);
),
Text(女)
]),
Row(children: <
Widget>
[
Radio(value: GenderType.FEMALE, groupValue: _groupValue, onChanged: null),
Text(不可选中)
])
]);
文章图片
materialTapTargetSize materialTapTargetSize 为默认 Radio 可选中点击的最小范围;主要分为 padded 和 shrinkWrap 两种状态,分析源码可以看到两者尺寸相差 8.0,因此 Radio 所在的范围是不可变更的,这也是小菜准备自定义 ACERadio 扩展方向之一;
switch (widget.materialTapTargetSize ?? themeData.materialTapTargetSize)
case MaterialTapTargetSize.padded:
size = const Size(2 * kRadialReactionRadius + 8.0, 2 * kRadialReactionRadius + 8.0);
break;
case MaterialTapTargetSize.shrinkWrap:
size = const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius);
break;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
Text(padded),
Container( color: Colors.grey.withOpacity(0.4),
child: Radio( value: GenderType.MALE, groupValue: _groupValue,
materialTapTargetSize: MaterialTapTargetSize.padded,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = https://www.songbingjia.com/android/val);
)),
]),
SizedBox(width: 10),
Row(children: <
Widget>
[
Container( color: Colors.grey.withOpacity(0.4),
child: Radio( value: GenderType.FEMALE, groupValue: _groupValue,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = val);
)),
Text(shrinkWrap)
])
]);
文章图片
activeColor activeColor 为单选框选中状态时绘制的颜色;若未设置,默认为 ThemeData.toggleableActiveColor 对应颜色;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
Radio( value: GenderType.MALE, groupValue: _groupValue,
activeColor: Colors.green,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = https://www.songbingjia.com/android/val);
),
Text(男, style: TextStyle( color: _groupValue == GenderType.MALE ? Colors.green : Colors.black))
]),
Row(children: <
Widget>
[
Radio( value: GenderType.FEMALE, groupValue: _groupValue,
activeColor: Colors.red,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = val);
),
Text(女, style: TextStyle( color: _groupValue == GenderType.FEMALE ? Colors.red : Colors.black))
])
]);
文章图片
focusColor & hoverColor focusColor / hoverColor 分别对应获取焦点时的颜色与点击高亮颜色;但小菜尝试了多次效果并不明显,因需求场景较少,暂不做处理;
未选中颜色 & 不可选颜色 Radio 并未提供未选中状态和不可选中状态按钮颜色;小菜分析源码,发现 未选中状态 与 ThemeData.unselectedWidgetColor 颜色对应,不可选中状态 与 ThemeData.disabledColor 对应;若需要动态修改这两种颜色状态,可以在对应的 Radio 外设置 ThemeData 对应的颜色状态即可;
return Theme(
data: ThemeData(unselectedWidgetColor: Colors.deepPurple, disabledColor: Colors.brown),
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
Radio( value: GenderType.MALE, groupValue: _groupValue,
activeColor: Colors.green,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = https://www.songbingjia.com/android/val);
),
Text(男, style: TextStyle( color: _groupValue == GenderType.MALE ? Colors.green : Colors.black))
]),
Row(children: <
Widget>
[
Radio( value: GenderType.FEMALE, groupValue: _groupValue,
activeColor: Colors.red,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = val);
),
Text(女, style: TextStyle( color: _groupValue == GenderType.FEMALE ? Colors.red : Colors.black))
]),
Row(children: <
Widget>
[
Radio( value: GenderType.FEMALE, groupValue: _groupValue, onChanged: null),
Text(不可选中)
])
]));
文章图片
ACERadio 为了更灵活的应用 Radio 单选框,小菜准备在此基础上扩展如下几个方面:
- 动态设置 未选中状态颜色;
- 动态设置 不可选中状态颜色;
- 动态设置 选中框按钮尺寸;
- 添加状态 取消按钮外边距;
小菜自定义了三种 ACEMaterialTapTargetSize 尺寸,增加了 zero 类型取消按钮外边距;
enum ACEMaterialTapTargetSizepadded, shrinkWrap, zero double radius = widget.radiosize ?? kRadialReactionRadius;
switch (widget.materialTapTargetSize ?? ACEMaterialTapTargetSize.padded)
case ACEMaterialTapTargetSize.padded:
size = Size(2 * radius + 8.0, 2 * radius + 8.0);
break;
case ACEMaterialTapTargetSize.shrinkWrap:
size = Size(2 * radius, 2 * radius);
break;
case ACEMaterialTapTargetSize.zero:
size = Size(radius, radius);
break;
小菜优先判断添加的未选中状态颜色和不可选中状态颜色;若未设置以 ThemeData 为准;
Color _getInactiveColor(ThemeData themeData)
return enabled ? widget.unCheckedColor ?? themeData.unselectedWidgetColor
: widget.disabledColor ?? themeData.disabledColor;
小菜添加一个 radioSize 属性,在绘制按钮时,按比例动态绘制按钮尺寸;
// Outer circle
final Paint paint = Paint()
..color = Color.lerp(inactiveColor, radioColor, position.value)..style = PaintingStyle.stroke
..strokeWidth = radioSize / 4 ?? 2.0;
canvas.drawCircle(center, radioSize ?? _kOuterRadius, paint);
// Inner circle
if (!position.isDismissed)
paint.style = PaintingStyle.fill;
canvas.drawCircle(center,
(radioSize != null ? radioSize * 4.5 / 8 : _kInnerRadius) * position.value, paint);
案例尝试
取消按钮外边距 Radio 默认提供了两种最小可点击范围,但小菜想取消按钮整体外边距,于是添加一种 ACEMaterialTapTargetSize.zero 方式来仅设置按钮尺寸;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
Text(padded),
Container(color: Colors.grey.withOpacity(0.4),
child: ACERadio(value: GenderType.MALE, groupValue: _groupValue,
materialTapTargetSize: ACEMaterialTapTargetSize.padded,
onChanged: (val) =>
setState(() =>
_groupValue = https://www.songbingjia.com/android/val))),
]),
Row(children: <
Widget>
[
Container(color: Colors.grey.withOpacity(0.4),
child: ACERadio(value: GenderType.FEMALE, groupValue: _groupValue,
materialTapTargetSize: ACEMaterialTapTargetSize.shrinkWrap,
onChanged: (val) =>
setState(() =>
_groupValue = val))),
Text(shrinkWrap)
]),
Row(children: <
Widget>
[
Container(color: Colors.grey.withOpacity(0.4),
child: ACERadio(value: GenderType.FEMALE, groupValue: _groupValue,
materialTapTargetSize: ACEMaterialTapTargetSize.zero,
onChanged: null)),
Text(zero)
])
]);
文章图片
未选中状态 & 不可选中状态 未选中状态 & 不可选中状态 可以通过 ThemeData 来动态修改,小菜为了方便,添加了 unCheckedColor & disabledColor 可直接进行设置;
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <
Widget>
[
Row(children: <
Widget>
[
ACERadio(
value: GenderType.MALE, groupValue: _groupValue,
activeColor: Colors.green, unCheckedColor: Colors.deepPurple,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = https://www.songbingjia.com/android/val);
),
Text(男, style: TextStyle( color: _groupValue == GenderType.MALE ? Colors.green : Colors.black))
]),
Row(children: <
Widget>
[
ACERadio(
value: GenderType.FEMALE, groupValue: _groupValue,
activeColor: Colors.red, unCheckedColor: Colors.deepPurple,
onChanged: (val)print(---onChanged---$val);
setState(() =>
_groupValue = val);
),
Text(女, style: TextStyle( color: _groupValue == GenderType.FEMALE ? Colors.red : Colors.black))
]),
Row(children: <
Widget>
[
ACERadio(
value: GenderType.FEMALE, groupValue: _groupValue,
disabledColor: Colors.brown, unCheckedColor: Colors.deepPurple,
onChanged: null),
Text(不可选中)
])
]);
文章图片
选中框按钮尺寸 Radio 单选框尺寸是固定的,小菜为了更方便的修改,添加了 radioSize 尺寸来动态修改按钮尺寸,且在动态设置按钮尺寸之后依旧支持最小点击范围的三种样式;
return Column(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
Row(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
ACERadio(radioSize: 6.0, value: SizeType.SIZE_6, groupValue: _groupSizeValue, onChanged: (val) =>
setState(() =>
_groupSizeValue = https://www.songbingjia.com/android/val)),
Text(Size:6.0*6.0)
]),
Row(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
ACERadio(radioSize: 8.0, value: SizeType.SIZE_8, groupValue: _groupSizeValue, onChanged: (val) =>
setState(() =>
_groupSizeValue = val)),
Text(Size:8.0*8.0)
]),
Row(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
ACERadio(radioSize: 10.0, value: SizeType.SIZE_10, groupValue: _groupSizeValue, onChanged: (val) =>
setState(() =>
_groupSizeValue = val)),
Text(Size:10.0*10.0)
]),
Row(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
ACERadio(radioSize: 14.0, value: SizeType.SIZE_14, groupValue: _groupSizeValue, onChanged: (val) =>
setState(() =>
_groupSizeValue = val)),
Text(Size:14.0*14.0)
]),
Row(mainAxisSize: MainAxisSize.min, children: <
Widget>
[
ACERadio(radioSize: 18.0, value: SizeType.SIZE_18, groupValue: _groupSizeValue, onChanged: (val) =>
setState(() =>
_groupSizeValue = val)),
Text(Size:18.0*18.0)
]),
]);
文章图片
ACERadio 案例源码
【Flutter 专题109 图解自定义 ACERadio 单选框 #yyds干货盘点#】 小菜对底层源码还不够深入,只是对 Radio 单选框的一点小扩展;如有错误,请多多指导!
推荐阅读
- Docker下的Spring Cloud三部曲之二(细说Spring Cloud开发)
- Docker容器实战六(构建定制化镜像)
- C#/VB.NET 在Excel单元格中应用多种字体格式
- Vmware Horizon( vmware horzion 介绍与实验需求)
- 高可用之故障隔离
- Redis笔记(简介源码安装常用命令配置文件内存管理)
- 大数据测试学习笔记之数据质量
- 浅谈(Java)并发ThreadLocal
- 实战(向GitHub提交代码时触发Jenkins自动构建)