简介
在iOS
原生开发的时候,Switch
是一个很简单的控件,使用起来非常方便。
但是,到了Flutter
中,关于Switch
却遇到了许多的困惑,带来了一定的混乱。
Android风格的Switch
![](https://img.haomeiwen.com/i1186939/dddd676233ee8cc7.png)
Switch({
Key? key,
required bool value,
required void Function(bool)? onChanged,
Color? activeColor,
Color? activeTrackColor,
Color? inactiveThumbColor,
Color? inactiveTrackColor,
ImageProvider<Object>? activeThumbImage,
void Function(Object, StackTrace?)? onActiveThumbImageError,
ImageProvider<Object>? inactiveThumbImage,
void Function(Object, StackTrace?)? onInactiveThumbImageError,
MaterialStateProperty<Color?>? thumbColor,
MaterialStateProperty<Color?>? trackColor,
MaterialStateProperty<Color?>? trackOutlineColor,
MaterialStateProperty<double?>? trackOutlineWidth,
MaterialStateProperty<Icon?>? thumbIcon,
MaterialTapTargetSize? materialTapTargetSize,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
MouseCursor? mouseCursor,
Color? focusColor,
Color? hoverColor,
MaterialStateProperty<Color?>? overlayColor,
double? splashRadius,
FocusNode? focusNode,
void Function(bool)? onFocusChange,
bool autofocus = false,
})
-
这么多的参数,想想都头皮发麻。真想不到,小小的
Switch
尽然要配置这么多的参数。 -
常用的参数也就
value
和onChanged
两个,实在想不明白,为何要那么多的配置参数。 -
在
Android
手机上,这个样子也就忍了。但是在苹果手机上,这个摸样确实太丑。
iOS风格的Switch
![](https://img.haomeiwen.com/i1186939/b70d2aaae8c33701.png)
CupertinoSwitch({
Key? key,
required bool value,
required void Function(bool)? onChanged,
Color? activeColor,
Color? trackColor,
Color? thumbColor,
bool? applyTheme,
Color? focusColor,
FocusNode? focusNode,
void Function(bool)? onFocusChange,
bool autofocus = false,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
})
-
参数少了很多,感觉好多了;
-
常用的参数也是
value
和onChanged
这两个; -
样子感觉好多了,就算在
Android
手机上,感觉也是不错的;
是否保持状态?
-
从
value
参数的角度来看,Switch
不应该保持状态;状态由父组件通过value
参数设置; -
从
onChanged
参数的角度来看,Switch
应该保持状态。随着不断点击Switch
组件,开关状态不断变化,并且通过onChanged
参数将变化回传给父组件。 -
两个必填参数,传递出两种不同的意图,到底要不要保持状态?
实际上
Switch
不能保留状态,但是确实能将变化后的状态传出来。并且传出来值必定跟传入的value
值相反
思路
-
在外观上,iOS风格的完胜;所以没有必要维持两套风格,取iOS的风格就好;
-
关于状态方面,干脆就支持两种使用方式;想不想保留状态,由父组件通过配置参数来决定。
参数设计
/// 模式1:给value参数,不保持状态,由父组件指定开关状态;
/// 模式2:不给value参数,自己记忆开关状态;
class PandaSwitch extends StatefulWidget {
const PandaSwitch({
Key? key,
this.initialValue,
this.value,
this.onChanged,
this.width,
this.height,
this.margin,
this.padding,
}) : super(key: key);
final bool? initialValue;
final bool? value;
final void Function(bool)? onChanged;
final double? width;
final double? height;
final EdgeInsetsGeometry? margin;
final EdgeInsetsGeometry? padding;
@override
State<PandaSwitch> createState() => _PandaSwitchState();
}
-
将
value
参数类型改为可选,通过父组件给不给这个参数来决定是否保留状态。 -
onChanged
参数类型也改为可选,降低使用要求。 -
width, height, margin, padding
这几个直接给Container
,为了布局方便。可以根据需求追加。
具体实现
基类
由于要保存状态,响应点击的变化,所以选择StatefulWidget
作为基类;
初始值
-
参数
initialValue
,就是用来设置初始值的; -
如果不设置,那就默认关闭;
-
只执行一次
@override
void initState() {
super.initState();
/// 初始值由外部指定;不指定的话,给默认值false
_isOn = widget.initialValue ?? false;
}
模式确定
-
由参数
value
是否为空来决定使用模式; -
每次
build
都判断一次,调整状态;
@override
Widget build(BuildContext context) {
/// 如果外部指定了值,那么强制设定为外部值
if (widget.value != null) {
_isOn = widget.value!;
}
///……
}
界面实现
- 使用图片表示状态
![](https://img.haomeiwen.com/i1186939/a2be3efe6ced5a10.png)
- 外面套个
Container
是为了使用的时候布局方便
参考代码
import 'package:flutter/material.dart';
import 'package:pandabuy/r.dart';
/// 模式1:给value参数,不保持状态,由父组件指定开关状态;
/// 模式2:不给value参数,自己记忆开关状态;
class PandaSwitch extends StatefulWidget {
const PandaSwitch({
Key? key,
this.initialValue,
this.value,
this.onChanged,
this.width,
this.height,
this.margin,
this.padding,
}) : super(key: key);
final bool? initialValue;
final bool? value;
final void Function(bool)? onChanged;
final double? width;
final double? height;
final EdgeInsetsGeometry? margin;
final EdgeInsetsGeometry? padding;
@override
State<PandaSwitch> createState() => _PandaSwitchState();
}
class _PandaSwitchState extends State<PandaSwitch> {
late bool _isOn;
@override
void initState() {
super.initState();
/// 初始值由外部指定;不指定的话,给默认值false
_isOn = widget.initialValue ?? false;
}
@override
Widget build(BuildContext context) {
/// 如果外部指定了值,那么强制设定为外部值
if (widget.value != null) {
_isOn = widget.value!;
}
var imageName = _isOn ? R.assetsImgSwitchOn : R.assetsImgSwitchOff;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
_isOn = !_isOn;
});
if (widget.onChanged != null) {
widget.onChanged!(_isOn);
}
},
child: Container(
color: Colors.transparent,
width: widget.width,
height: widget.height,
margin: widget.margin,
padding: widget.padding,
child: Center(
child: Image.asset(imageName),
),
),
);
}
}
使用的例子
PandaSwitch(
margin: EdgeInsets.only(left: 5.w),
value: ((pi['unPackWeightSelect'] ==
'1') ||
(pi['unPackWeightSelect'] == '2')),
onChanged: (value) {
if (pi['unPackWeightSelect'] == '2') {
return;
}
logic.unPackClick(pi, value);
},
),
-
指定了
value
,显然是无状态的使用方式;开关状态由父组件决定; -
如果要记忆状态,可以不给
value
参数,而是给initialValue
参数(不给的话默认是false
) -
Container
能带来方便,不然的话,外面就要套一个,或者加个占位的SizedBox
网友评论