自定义单选按钮
一个可动画的单选按钮,可以最大程度地进行自定义。
我发现 Flutter 只提供两个单选按钮小部件:Radio 和 RadioListTile 令人奇怪。这两个小部件的主要问题是它们都强制使用了默认的 Android 开头动画圆形图标。该小部件通过允许用户提供自己的构建器函数来处理所有内容。此外,还可以提供动画构建器。这会传递一个父级动画控制器,用户可以使用该控制器来创建一系列动画,以动画化小部件在状态之间的过渡。
安装
只需在 pubspec.yaml 文件中将 custom_radio: ^0.1.2 添加为依赖项。
然后,在需要它的任何地方 import 'package:custom_radio/custom_radio.dart';。
示例

如果只需要一种动画类型,则可以将其指定为启用更强的类型。
CustomRadio<String, double>(
value: 'First',
groupValue: widget.radioValue,
duration: Duration(milliseconds: 500),
animsBuilder: (AnimationController controller) => [
CurvedAnimation(
parent: controller,
curve: Curves.easeInOut
)
],
builder: (BuildContext context, List<double> animValues, Function updateState, String value) {
final alpha = (animValues[0] * 255).toInt();
return GestureDetector(
onTap: () {
setState(() {
widget.radioValue = value;
});
},
child: Container(
padding: EdgeInsets.all(32.0),
margin: EdgeInsets.all(12.0),
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor.withAlpha(alpha),
border: Border.all(
color: Theme.of(context).primaryColor.withAlpha(255 - alpha),
width: 4.0,
)
),
child: Text(
value,
style: Theme.of(context).textTheme.body1.copyWith(fontSize: 24.0),
)
)
);
}
)

但任何动画类型的组合都得到支持。
CustomRadio<String, dynamic>(
value: 'First',
groupValue: widget.radioValue,
animsBuilder: (AnimationController controller) => [
CurvedAnimation(
parent: controller,
curve: Curves.easeInOut
),
ColorTween(
begin: Colors.white,
end: Colors.deepPurple
).animate(controller),
ColorTween(
begin: Colors.deepPurple,
end: Colors.white
).animate(controller),
],
builder: (BuildContext context, List<dynamic> animValues, Function updateState, String value) {
return GestureDetector(
onTap: () {
setState(() {
widget.radioValue = value;
});
},
child: Container(
alignment: Alignment.center,
margin: EdgeInsets.all(18.0),
padding: EdgeInsets.all(32.0 + animValues[0] * 12.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: animValues[1],
border: Border.all(
color: animValues[2],
width: 2.0
)
),
child: Text(
value,
style: Theme.of(context).textTheme.body1.copyWith(
fontSize: 20.0,
color: animValues[2]
),
)
)
);
},
)

您甚至可以重现Radio提供的默认动画,并添加您自己的个性化风格!
注意:完整示例可在 example 目录中找到
CustomRadio<int, double>(
value: value,
groupValue: widget.radioValue,
duration: Duration(milliseconds: 400),
animsBuilder: (AnimationController controller) => [
CurvedAnimation(
parent: controller,
curve: Curves.ease
)
],
builder: ({ BuildContext context, List<double> animValues, Function updateState, bool checked }) {
return GestureDetector(
onTapDown: (TapDownDetails details) {
setState(() {
if (_controller.status != AnimationStatus.completed)
_controller.forward();
});
},
onTapUp: (TapUpDetails details) {
setState(() {
if (_controller.status != AnimationStatus.dismissed)
_controller.reverse();
});
},
onTap: () {
setState(() {
widget.radioValue = value;
});
},
child: Container(
margin: EdgeInsets.all(8.0),
width: 38.0,
height: 38.0,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
width: 38.0 * _animation.value,
height: 38.0 * _animation.value,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor.withAlpha(40)
),
),
Container(
width: 18.0,
height: 18.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(
color: checked ? Theme.of(context).primaryColor : Theme.of(context).hintColor,
width: 2.0
)
),
),
Container(
width: 11.0 * animValues[0],
height: 11.0 * animValues[0],
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryColor,
),
),
]
),
)
);
}
)
