我们在上一章回中介绍了"SlideSwitch组件"相关的内容,本章回中将介绍 自定义SlideImageSwitch闲话休提,让我们一起Talk Flutter吧。
1. 概念介绍
我们在前面章回中介绍了Switch组件相关的内容,并且在pub.dev中找过许多Switch类组件,这些组件中大部分都是修改开和关两种状态的风格,或者像官方Switch
一样修改滑块风格,或者把开关状态的风格放到滑块上。目前还没有找到一种组件可以同时修改轨道和滑块的组件。于是我们只好自定义符合自己需求的组件。
本章回中介绍的SlideImageSwitch
就是我们自定义的组件,该组件是一种通过图片做的滑动开关,它的功能和官方的Switch相同,只是它完全通过图片实现。它的轨道和滑块都是图片。下面是该组件的运行效果图,请大家参考。图中的的轨道(track)是一张图片,滑块(thumb)也是一张图片。本章回中将介绍如何去实现这样的组件。
2. 思路与方法
2.1 实现思路
我们在本章回中自定义的Switch完全通过图片来实现,因此可以通过Stack
组合两个Image组件,一个用来表示滑块,另外一个用来表示轨道。这样便把Switch的外观创建好了,不过它还不能滑动。因此我们需要在Stack外层嵌套一个手势组件用来响应滑动事件,有滑动事件时移动滑块。
2.2 实现方法
我们在上面的小节中介绍自定义的Switch的思路,下面将介绍具体的实现方法:
- 创建Stack组件,并且用它包含两个Image组件;
- 使用Positioned控制滑块图片的位置,便于移动滑块;
- 在Stack组件外层添加GestureDetector组件来响应滑动事件;
- 在GestureDetector组件的滑动方法中获取滑动事件,并且移动滑块;
- 创建一个方法类型的参数,在滑块移动结束时调用该方法,把开关状态传递到组件外;
上面介绍的实现方法看着简单,但是还有一些细节需要注意:
- 创建Image组件时最好把它们放到自定义组件的参数中,这样操作方便修改轨道和滑块的图片;
- 在滑动事件中移动滑块时只需要按照水平或者垂直方向中的任意一个来移动滑动就可以;
- 在移动滑块时要注意滑块的移动范围,不能无限制移动,确保它在轨道范围内移动;
- 向外面回传开关状态值时一定在在滑块移动结束后回传,不能在移动过程中回传;
3. 示例代码
class SlideImageB extends StatefulWidget {
const SlideImageB({super.key, required this.width, required this.height,
required this.trackImage, required this.thumbImage, required this.onValueChanged});
///这个长度和宽度是容器的大小也是图片的大小,图片不够时会进行拉伸。这样操作的缺点是没有边距
final double width;
final double height;
///轨道和滑块的图片
final ImageProvider<Object> trackImage;
final ImageProvider<Object> thumbImage;
final ValueChanged<bool>? onValueChanged;
State<SlideImageB> createState() => _SlideImageBState();
}
class _SlideImageBState extends State<SlideImageB> {
///控制滑动的范围,它是背景图片与滑动图片高度的差值,也就是未滑动前可以看到的背景图片大小
double scrollRange = 60;
///这个值默认是scrollRange的一半
double slideValue = 60;
bool switchOn = false;
Widget build(BuildContext context) {
return Container(
color: Colors.green,
width: widget.width,
height: widget.height,
///使用Listener和GestureDetector都可以
child: GestureDetector(
child: Stack(
alignment: Alignment.center,
children: [
///背景图片
Image(
width: widget.width,
height: widget.height,
image: widget.trackImage,
fit: BoxFit.fill,
),
///可以滑动的图片
///使用Position控制坐标
Positioned(
left: 0,
top: slideValue,
child: Image(
width: widget.width,
height: widget.height-scrollRange,
image: widget.thumbImage,
fit: BoxFit.fill,
),
),
],
),
///响应滑动事件
onPanUpdate: (event){
setState(() {
slideValue += event.delta.dy;
if(slideValue > scrollRange) {
slideValue = scrollRange;
switchOn = true;
}
if(slideValue < 0) {
slideValue = 0;
switchOn = false;
}
});
},
///通过回调传递开关的结果需要在手势滑动结束后,不能在手势滑动过程中
onPanEnd: (details){
///使用了call语法
widget.onValueChanged?.call(switchOn);
},
),
);
}
}
上面的示例代码是按照上一小节中介绍的实现方法实现的,我们在代码关键位置添加了注释,这样方便大家理解代码,大家也可以参考实现方法来分析和理解代码。
此外,我们在代码中还添加了一个容器组件,它主要用来控制组件的大小,这个容器可有可无,因此在实现方法中没有提到。我们把自定义的组件封装成了独立的组件,大家可以直接拿来使用,只需要在使用时传入必须的参数就可以。
4. 内容总结
最后,我们对本章回的内容做一个全面的总结:
- 自定义滑动Switch通过Stack组合两个Image组件实现;
- 自定义组件中通过GestureDetector组件控制滑块移动;
- 移动滑块时可以沿着水平或者垂直方向中的任意一个方向移动;
- 在滑块移动结束后需要把自定义组件的开关状态值传递到组件外;
经验分享
此外,我们在这里分享一些项目经验给大家:在使用stack
和Positioned
移动滑块时它们两种组件需要配合使用,并且在Positioned组件中指定Iamge的高度和宽度,否则有以下错误:
Vertical viewport was given unbounded height
看官们,与"自定义SlideImageSwitch"相关的内容就介绍到这里,欢迎大家在评论区交流与讨论!