前言
最近在做一个需要自定义Slider组件的功能,因为原生的那个不能配置相关的Circle 以及宽高,既然不能,那我们自己重写一个
思路
基础功能
在开始实现之前,我们先来想一想一个滑动条拥有那些基础功能
当然还可以添加其他属性,如果你是需要做缩放功能的可以加一个scale
缩放值 与 点位同步
这个点是非常重要的,也是重点功能!
本文自定义的是一个控制缩放的滑动条,以下参数值皆为示例参考,可根据实际场景自行修改
1.scale最大值为4.0,scale最小值为1.0
2.pointX最大偏移量为(外层父级最大宽度 – 15)
点位与缩放同步的规律则为
Point - > 0-9 Scale -> 1.0 -1.9 Point -> 10 -20 Scale -> 2.0 - 3.0 Point -> 20 - 30 Scale -> 3.0 - > 4.0
Point代表在水平触摸的实际点位dx偏移量,Scale代表实际缩放量
通过这个规律我们可以得出
在点位信息小于10的情况下,我们则以最小缩放值1.0取值范围内递增0.1
在点位信息超出10的情况下,我们则以每次(点位信息/10)方式并不能直接满足!
需要以小数点前的具体点位 /10之后,再+1.0
注:
为什么要采用小于10的时候取值范围每次递增0.1呢?
因为当PointX为10时,根据Point/10 = 1.0则缩放值从头1.0开始,而当点位为10时或者大于10的情况下,我们要在每次小数点前的具体点位值+1.0,则PointX = 10 Scale = 2.0 ,避免了重头开始情况!实现手段
创建一个StatefulWidget
新增 ScaleSlider 因为缩放需要实时更新状态,所以我们选择 StatefulWidget
ScaleSlider.dartclass ScaleSlider extends StatefulWidget { //这里因为我使用的是GetX框架,这里携带了logic & state,实际应用中可以不需要携带,缩放值在state中 final ReadLogic readLogic; final ReadState readState; final double max; final double min; final ValueChanged<double> onChanged; final bool flag; const ScaleSlider(this.readLogic, this.readState, this.max, this.min, this.flag, this.onChanged, {Key? key}) : super(key: key); @override State<StatefulWidget> createState() { return ScaleSliderState(); } }
State
ScaleSliderState
class ScaleSliderState extends State<ScaleSlider>{
String assetsPrefix = "assets/new_blue/txt/index/scale/";
double _value = 0.0;
GlobalKey globalKey = GlobalKey();
GlobalKey globalKey2 = GlobalKey();
double maxOffsetX = 0;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
Log.i("页面加载完毕!");
//根据全局Key获取最大偏移量
final RenderBox renderBox =
globalKey.currentContext!.findRenderObject() as RenderBox;
Size targetSize = renderBox.size;
maxOffsetX = targetSize.width - 15;
print('最大偏移量: ${maxOffsetX}');
});
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
double scaleResult = (widget.readState.scale * 10);
return GestureDetector(
onHorizontalDragUpdate: (details) {
double pointsX = details.localPosition.dx;
print('当前位置:${details.localPosition.dx}');
double minPoints = 0;
double scaleValue = 0;
scaleValue = (pointsX / 10);
if(details.localPosition.dx >=maxOffsetX){
_value = maxOffsetX;
pointsX = maxOffsetX;
scaleValue = widget.max;
print('到最大!');
}
else if(details.localPosition.dx <=minPoints){
_value = 0;
pointsX = widget.min;
scaleValue = widget.min;
print('到最小!');
}else{
String pointXString = pointsX.toString();
//如果点位小于10的全部视为缩放率1.N小数
if(pointsX < 10){
scaleValue = (widget.min) + scaleValue;
}else{
//小数点前具体数额
String subStartNumber = pointXString.substring(0,pointXString.indexOf("."));
//转比例
double targetNumber = double.parse(subStartNumber)/ 10;
double finishValue = (targetNumber + 1.0);
scaleValue = finishValue;
}
}
setState(() {
_value = pointsX;
widget.readState.validScaleControl.value = false;
});
Log.i("当前点位信息: ${pointsX},缩放率:${scaleValue}");
widget.onChanged(scaleValue);
},
child: Container(
key: globalKey,
child: Stack(
children: [
// 底部条
Positioned(
left: 0.rpx,
top: 18.rpx,
child: SizedBox(
height: 5.rpx,
// width: Get.width,
child: Image.asset(
"${assetsPrefix}slider.png",
fit: BoxFit.fill,
),
),
),
// 滚动条
Positioned(
left: widget.flag
? scaleResult <= (widget.min * 10)
? 0
: scaleResult >= (widget.max * 10)
? maxOffsetX
: scaleResult
: _value,
top: 5.rpx,
child: SizedBox(
width: 30.rpx,
height: 30.rpx,
child: Image.asset("${assetsPrefix}circle.png"),
),
),
],
),
));
}
}
结束语
这个自定义功能具体说难不难,主要是根据实际应用场景去更改组件具体实现算法或者逻辑,如果你有更好的想法欢迎评论!