详细可以访问仓库 HcUi: 重复创造Flutter 的轮子 在原有组件上拓展 展现出新的特性 (gitee.com)
介绍
Switch组件的增强版.用于在打开和关闭状态之间进行切换。支持更换背景/增加文字 Icon等
![](https://img.haomeiwen.com/i27902463/8455bb225f6ce8ff.gif)
pt304-mte45.gif
代码演示
基础用法
HcSwitch(
onChanged: (bool value) {
return Future.value(!value);
},
value: true,
),
修改圆角
HcSwitch(
radius: 3,
onChanged: (bool value) {
return Future.value(!value);
},
value: true,
),
禁用状态
HcSwitch(
onChanged: (bool value) {
return Future.value(!value);
},
value: true,
disabled: true,
),
修改颜色
HcSwitch(
radius: 3,
inactiveThumbColor: Colors.yellow,
inactiveTrackColor: Colors.grey,
activeColor: Colors.purple,
activeTrackColor: Colors.green,
onChanged: (bool value) {
return Future.value(!value);
},
value: false,
),
修改大小
HcSwitch(
radius: 3,
size:48,
inactiveThumbColor: Colors.yellow,
inactiveTrackColor: Colors.grey,
activeColor: Colors.purple,
activeTrackColor: Colors.green,
onChanged: (bool value) {
return Future.value(!value);
},
value: false,
),
显示文字
HcSwitch(
activeWidget: Text("开"),
inactiveWidget: Text("关"),
showPosition: HcSwitchShowPosition.thumb,
activeColor: Colors.purple,
activeTrackColor: Colors.green,
onChanged: (bool value) {
return Future.value(!value);
},
value: true,
),
修改按钮组件
HcSwitch(
thumbWidget: HcImage(
src: "assets/images/icon.jpeg",
),
inactiveWidget: Text("关"),
showPosition: HcSwitchShowPosition.track,
onChanged: (bool value) {
return Future.value(!value);
},
value: true,
),
增加Loading动画
HcSwitch(
radius: 3,
thumbWidget: HcImage(
src: "assets/images/icon.jpeg",
borderRadius: BorderRadius.circular(3),
),
inactiveThumbColor: Colors.yellow,
inactiveTrackColor: Colors.grey,
showLoading: true,
activeWidget: Text("开"),
inactiveWidget: Text("关"),
showPosition: HcSwitchShowPosition.all,
onChanged: (bool value) async {
print("value${value}");
await Future.delayed(Duration(seconds: 2), () {
print('延迟2秒后执行');
});
return Future.value(!value);
},
value: false,
),
API
props
参数 |
说明 |
类型 |
默认值 |
是否必填 |
value |
switch的状态 |
bool |
false |
false |
onChanged |
点击后的回调 |
HcSwitchChange |
- |
true |
disabled |
是否禁用 |
bool |
- |
false |
activeColor |
激活状态下按钮颜色 |
color |
- |
false |
activeTrackColor |
激活状态下轨道颜色 |
color |
- |
false |
inactiveThumbColor |
非激活状态下按钮颜色 |
color |
- |
false |
inactiveTrackColor |
非激活状态下按钮颜色 |
color |
- |
false |
thumbWidget |
按钮的组件 |
Widget |
- |
false |
showLoading |
是否展示加载中动画 |
bool |
false |
false |
size |
按钮的大小 |
double |
20.0 |
false |
radius |
按钮的圆角 |
double |
10.0 |
false |
padding |
轨道和按钮中间的距离 |
double |
1.0 |
false |
activeWidget |
激活时显示的组件(text/icon) |
Widget |
- |
false |
inactiveWidget |
未激活时显示的组件 (text/icon) |
Widget |
- |
false |
showPosition |
显示组件的位置 |
HcSwitchShowPosition |
- |
false |
duration |
动画时间 |
Duration |
300ms |
false |
HcSwitchShowPosition
展示activeWidget/inactiveWidget组件的位置
参数名 |
说明 |
none |
不展示 |
track |
只展示在轨道上 |
thumb |
只展示在按钮上 |
all |
展示按钮和轨道上 |
Function
方法名 |
说明 |
参数 |
返回类型 |
HcSwitchChange |
滑动/点击后的回调 |
Function(bool) |
Future<bool?> |
项目源码
//展示组件的位置 none不显示 track轨道上 thumb 按钮上 ,all全部
enum HcSwitchShowPosition { none, track, thumb, all }
typedef HcSwitchChange = Future<bool?> Function(bool);
class HcSwitch extends StatefulWidget {
//点击后的回调
final HcSwitchChange onChanged;
//当前状态
final bool value;
//是否禁用
final bool disabled;
//激活状态下按钮颜色
final Color? activeColor;
//激活状态下轨道颜色
final Color? activeTrackColor;
//关闭状态下按钮颜色
final Color? inactiveThumbColor;
//关闭状态下轨道颜色
final Color? inactiveTrackColor;
//按钮的组件
final Widget? thumbWidget;
//是否展示加载中
final bool showLoading;
//按钮大小
final double size;
//组件和按钮的圆角
final double? radius;
//轨道和按钮间的距离
final double padding;
//激活状态显示的组件
final Widget? activeWidget;
//未激活状态显示的组件
final Widget? inactiveWidget;
//显示组件的位置
final HcSwitchShowPosition showPosition;
//动画时间
final Duration? duration;
const HcSwitch(
{Key? key,
required this.onChanged,
this.value = false,
this.disabled = false,
this.activeColor,
this.activeTrackColor,
this.inactiveThumbColor,
this.inactiveTrackColor,
this.thumbWidget,
this.showLoading = false,
this.size = 20,
this.radius,
this.duration,
this.padding = 3,
this.activeWidget,
this.inactiveWidget,
this.showPosition = HcSwitchShowPosition.none})
: super(key: key);
@override
State<HcSwitch> createState() => _HcSwitchState();
}
class _HcSwitchState extends State<HcSwitch> {
//手指滑动的距离
double dragDistance = 0;
//当前选中的状态
bool value = false;
//当前是否展示加载中效果
bool showLoading = false;
//圆角
double radius = 0;
//需要展示的 组件列表
List<Widget> widgetList = List.empty();
@override
void initState() {
// TODO: implement initState
super.initState();
value = widget.value;
radius = widget.radius ?? (widget.size + widget.padding * 2) / 2;
widgetList = [
widget.activeWidget ?? const Text(""),
widget.inactiveWidget ?? const Text(""),
];
if (widget.showPosition == HcSwitchShowPosition.all) {
widgetList = widgetList.reversed.toList();
}
}
@override
void didUpdateWidget(HcSwitch oldWidget) {
super.didUpdateWidget(oldWidget);
widgetList = [
widget.activeWidget ?? const Text(""),
widget.inactiveWidget ?? const Text(""),
];
if (widget.showPosition == HcSwitchShowPosition.all) {
widgetList = widgetList.reversed.toList();
}
setState(() {
value = widget.value;
radius = widget.radius ?? (widget.size + widget.padding * 2) / 2;
widgetList = widgetList;
});
}
@override
Widget build(BuildContext context) {
return Semantics(
toggled: value,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(radius),
),
child: Stack(
children: [_trackWidget(), _thumbWidget()],
),
),
);
}
//轨道构建组件
Widget _trackWidget() {
Color trackColor = value
? (widget.activeTrackColor ?? Theme.of(context).primaryColor)
: (widget.inactiveTrackColor ?? Colors.grey);
return GestureDetector(
excludeFromSemantics: true,
onTap: _onTap,
child: AnimatedContainer(
width: widget.size * 2 + widget.padding * 2,
height: widget.size + widget.padding * 2,
decoration: BoxDecoration(
color: trackColor.withOpacity(widget.disabled
? HcSize.defaultSwitchDisableOpacity
: HcSize.defaultSwitchOpacity),
borderRadius: BorderRadius.circular(radius),
),
duration: widget.duration ?? const Duration(milliseconds: 500),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: widget.showPosition == HcSwitchShowPosition.track ||
widget.showPosition == HcSwitchShowPosition.all
? widgetList
.map((child) => _buildListItem(child, trackColor))
.toList()
: [const SizedBox(), const SizedBox()],
),
),
);
}
Widget _buildListItem(Widget child, Color color) {
final ThemeData theme = Theme.of(context);
final TextStyle effectiveTextStyle = theme.useMaterial3
? theme.textTheme.titleMedium!
: theme.primaryTextTheme.titleMedium!;
TextStyle textStyle =
effectiveTextStyle.copyWith(color: color, fontSize: widget.size / 2);
switch (ThemeData.estimateBrightnessForColor(color)) {
case Brightness.dark:
textStyle = textStyle.copyWith(color: theme.primaryColorLight);
break;
case Brightness.light:
textStyle = textStyle.copyWith(color: theme.primaryColorDark);
break;
}
return SizedBox(
width: widget.size,
height: widget.size,
child: Center(
child: MediaQuery(
// Need to ignore the ambient textScaleFactor here so that the
// text doesn't escape the avatar when the textScaleFactor is large.
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: IconTheme(
data: theme.iconTheme
.copyWith(color: textStyle.color, size: widget.size / 2),
child: DefaultTextStyle(
style: textStyle,
child: child,
),
),
),
),
);
}
//顶部按钮组件
Widget _thumbWidget() {
Color bgColor = value
? (widget.activeColor ?? Theme.of(context).colorScheme.secondary)
: (widget.inactiveThumbColor ?? Colors.white);
//动画组件
return AnimatedPositioned(
left: !value ? 0 : 1 * widget.size,
duration: widget.duration ?? const Duration(milliseconds: 100),
child: GestureDetector(
excludeFromSemantics: true,
//滑动开始
onHorizontalDragStart: _onHorizontalDragStart,
//滑动更新
onHorizontalDragUpdate: _onHorizontalDragUpdate,
//滑动结束
onHorizontalDragEnd: _onHorizontalDragEnd,
//点击事件
onTap: _onTap,
child: AnimatedContainer(
width: widget.size,
height: widget.size,
margin: EdgeInsets.all(widget.padding),
decoration: BoxDecoration(
color: bgColor, borderRadius: BorderRadius.circular(radius)),
duration: const Duration(milliseconds: 300),
child: Center(
child: !showLoading
? widget.thumbWidget ??
(widget.showPosition.index <
HcSwitchShowPosition.thumb.index
? const Text("")
: widgetList
.map((child) => _buildListItem(child, bgColor))
.toList()[value ? 1 : 0])
: Padding(
padding: EdgeInsets.all(widget.size / 5),
child: CircularProgressIndicator(
strokeWidth: widget.size / 20,
color: HcColorUtil.isLightColor(bgColor)
? Theme.of(context).primaryColorDark
: Theme.of(context).primaryColorLight,
),
),
),
),
));
}
//点击事件
void _onTap() {
dragDistance = widget.size * (value ? -1 : 1);
changeState();
}
//手指结束
void _onHorizontalDragEnd(details) => changeState();
//手指更新
void _onHorizontalDragUpdate(value) => dragDistance = value.localPosition.dx;
//手指滑动开始
void _onHorizontalDragStart(value) => dragDistance = 0;
changeState() async {
if (widget.disabled) return;
if ((value && dragDistance > 0) || (!value && dragDistance < 0)) {
return;
}
//如果参数不为空
if (widget.showLoading) {
setState(() {
showLoading = true;
});
}
bool result = !value;
try {
result = (await widget.onChanged.call(value)) ?? !value;
} finally {}
setState(() {
value = result;
showLoading = false;
});
}
}
网友评论