介绍
Flutter - 仿Airbnb的价格区间筛选器。(二)
Flutter-CustomPaint 绘制贝塞尔曲线图表(三)
整体结构
我尽量将介绍写入到注释里,这样方便与代码关联阅读理解起来更方便。
Stack(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
width: rootWidth,
height: rootHeight*0.45,
child: Stack(
children: <Widget>[
//bottom chart 底层 背景chart
buildLineChart(),
//above chart 上层 chart
ClipPath(
clipper: ChartClipPath(Size(30,20),//可以根据设计图也可以通过key传出来,这里方便起见
leftSlideImageOffset, rightSlideImageOffset),
child: buildLineChart(),
),
],
),
),
/// 价格滑块widget
Positioned(
bottom: 10,
child: SliderPrice(list: beanList,rootWidth: rootWidth,rootHeight: rootHeight * 0.4,
leftSlidListener: (dragging,index,leftImageKey){
//debugPrint("left ------- $dragging ------- $index");
leftSlideImageOffset = calculateSlideBlockInfo(leftImageKey);
setState(() {
});
},
rightSlidListener: (dragging,index,rightImageKey){
//debugPrint("right ------- $dragging ------- $index");
rightSlideImageOffset = calculateSlideBlockInfo(rightImageKey);
setState(() {
});
},),
),
///
],
)
ClipPath
可以看到这里我用了ClipPath对上层chart进行了包裹,通过传入一个Clipper(继承CustomClipper)可以对子widget进行裁剪。
另外官方还提供了ClipOval、ClipRect、ClipRRect来方便进行圆形、圆角矩形等的裁剪。
自定义Clipper 需要重写两个方法:
1,getClip(Size size),参数size是父widget给我们的最大尺寸。在这个方法里我们可以定义path并任意编辑,然后返回即可,最终会作用于子widget。
2,shouldReclip(),widget(element)刷新时是否重绘,这里要返回true。
ChartClipPath 代码:
class ChartClipPath extends CustomClipper<Path>{
//滑块按钮大小
Size slideBlockSize;
//左右滑块的位置
Offset leftBlockOffset,rightBlockOffset;
ChartClipPath(this.slideBlockSize, this.leftBlockOffset,
this.rightBlockOffset);
//因为一些适配原因,在这里加了个ratio方便调整,实际上如果你时间充裕,慢慢写可以完全舍弃掉这个变量...催得紧没办法
final double ratio =0.45;
@override
Path getClip(Size size) {
var path = Path();
//避免第一针绘制时一些值是空,所以进行不裁剪操作
if(slideBlockSize == null || leftBlockOffset == null || rightBlockOffset == null){
path.moveTo(0, 0);
path.lineTo(0,size.height);
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
path.close();
return path;
}
//这里很简单,实际上是根据左右滑块按钮的位置,走一个矩形path,然后用于裁剪
//这里要注意moveTo方法,例如A到B之间是没有path生成的
// 而 lineTo方法是会生成path的。
//path有很多子方法,贝塞尔曲线也是通过这里绘制的,实际上这里跟安卓原生非常像。
//起始点
path.moveTo(leftBlockOffset.dx - slideBlockSize.width * ratio, 0);
//向下
path.lineTo(leftBlockOffset.dx - slideBlockSize.width * ratio, leftBlockOffset.dy);
//向右
path.lineTo(rightBlockOffset.dx - slideBlockSize.width * ratio, rightBlockOffset.dy);
//向上
path.lineTo(rightBlockOffset.dx - slideBlockSize.width * ratio, 0);
//形成从终点到起点的闭合。(这里是直线所以要确保是你想要的)
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
到这里整个制作就完成了。
后话
以上只是UI绘制和功能交互,实际上结合业务需求,状态处理非常复杂,我的在实际开发中是用provider进行的控制,建议有兴趣的朋友可以了解一下,非常方便。(也可以尝试inheritedwidget,provider就是基于此实现的)
##DEMO
https://github.com/bladeofgod/chart_price_slider