【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

Radio 单选框在日常应用中很常见,Flutter 提供的单选框与 Android 提供的略有不同,和尚简单了解一下并对其进行部分扩展;
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
https://xueqiu.com/1453886689...
【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

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 = 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('不可选中')

])

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

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 = 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')

  ])

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

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 = 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))

  ])

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

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 = 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('不可选中')

      ])

    ]));

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

ACERadio

      为了更灵活的应用 Radio 单选框,和尚准备在此基础上扩展如下几个方面:

  • 动态设置 未选中状态颜色;
  • 动态设置 不可选中状态颜色;
  • 动态设置 选中框按钮尺寸;
  • 添加状态 取消按钮外边距;

源码扩展

      和尚自定义了三种 ACEMaterialTapTargetSize 尺寸,增加了 zero 类型取消按钮外边距;

enum ACEMaterialTapTargetSize { padded, 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 = 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')

  ])

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

未选中状态 & 不可选中状态

      未选中状态 & 不可选中状态 可以通过 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 = 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('不可选中')

  ])

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

选中框按钮尺寸

      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 = 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')

  ]),

]);

【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框

以上是 【Java】【Flutter 专题】109 图解自定义 ACERadio 单选框 的全部内容, 来源链接: utcz.com/a/96232.html

回到顶部