前言
flutter实现一个刻度尺,和大家一起学习探讨。
先上效果图:
概述
这个demo主要用到了flutter的自定义组件,无论是vue,android亦或者是flutter,
前端自定义组件的思路大致都相同。
都是从
1.封装原有组件
2.组合原有组件暴露方法接口
3.利用绘制实现自定义高的组件(Flutter中的绘制也是依靠Canvas和Paint来实现的)
在Flutter中使用自绘方式自定义Widget,大致需要以下步骤:继承CustomPainter并重写paint方法和shouldRepaint方法,在写paint方法中绘制内容,使用CustomPaint来构建Widget
class MyPainter extends CustomPainter {
final int subGridWidth;
final String valueStr;
//0:列表首item 1:中间item 2:尾item
final int type;
final Color colorType;
Paint _linePaint;
double _lineWidth = 2;
MyPainter(this.subGridWidth, this.valueStr, this.type,
this.colorType) {
_linePaint = Paint()
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = _lineWidth
..color = colorType;
}
@override
void paint(Canvas canvas, Size size) {
drawLine(canvas, size);
drawText(canvas, size);
}
void drawLine(Canvas canvas, Size size) {
double startY, endY;
switch (type) {
case 0: //首元素只绘制上半部分
startY = size.height / 2;
endY = size.height;
break;
case 2: //尾元素只绘制下半部分
startY = 0;
endY = size.height / 2;
break;
default: //中间元素全部绘制
startY = 0;
endY = size.height;
}
//绘制竖线
for (double y = startY; y <= endY; y += subGridWidth) {
if (y == size.height / 2) {
//中间为长刻度
canvas.drawLine(Offset(size.width - 100, y),
Offset(size.width - 100 + size.height * 3 / 5, y), _linePaint);
} else {
//其他为短刻度
canvas.drawLine(Offset(size.width - 100, y),
Offset(size.width - 100 + size.height / 3, y), _linePaint);
}
}
}
void drawText(Canvas canvas, Size size) {
//文字水平方向居中对齐,竖直方向底对齐
ui.Paragraph p = _buildText(valueStr, size.width);
//获得文字的宽高
double halfWidth = p.minIntrinsicWidth / 2;
double halfHeight = p.height / 2;
canvas.drawParagraph(
p, Offset(size.width - 160, size.height / 2 - halfHeight));
}
ui.Paragraph _buildText(String content, double maxWidth) {
ui.ParagraphBuilder paragraphBuilder =
ui.ParagraphBuilder(ui.ParagraphStyle());
paragraphBuilder.pushStyle(
ui.TextStyle(
fontSize: 14,
color: colorType,
//fontFamily: "Montserrat",
),
);
paragraphBuilder.addText(content);
ui.Paragraph paragraph = paragraphBuilder.build();
paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
return paragraph;
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
其中
_linePaint = Paint()
//是否启动抗锯齿
..isAntiAlias = true
//绘画风格,默认为填充,有fill和stroke两种
..style = PaintingStyle.stroke
//画笔宽度
..strokeWidth = _lineWidth
// 画笔颜色
..color = colorType;
通过创建一个段落构建器ui.ParagraphBuilder绘制字符串部分
//字体样式
paragraphBuilder.pushStyle(
ui.TextStyle(
fontSize: 14,
color: colorType,
//fontFamily: "Montserrat",
),
);
paragraphBuilder.addText(content);
ui.Paragraph paragraph = paragraphBuilder.build();
//限制绘制字符串宽度
paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
标尺一共有十个刻度,但是绘制的时候绘制11个刻度。第一个和最后一个只绘制一半的刻度。其他的全部绘制。目的将刻度尺的数值点(如:10分)对应的长刻度放在item的中间,便于绘制。
switch (type) {
case 0: //首元素只绘制上半部分
startY = size.height / 2;
endY = size.height;
break;
case 2: //尾元素只绘制下半部分
startY = 0;
endY = size.height / 2;
break;
default: //中间元素全部绘制
startY = 0;
endY = size.height;
}
利用ListView绘制11个自定义组件打到10个刻度单位的标尺。
ListView.builder(
physics: ClampingScrollPhysics(),
padding: EdgeInsets.all(0),
scrollDirection: Axis.vertical,
itemCount: 11,
itemBuilder: (BuildContext context, int index) {
//首尾空白元素
int type;
Color colorType;
//第一个普通元素
if (index == 0) {
type = 0;
//最后一个普通元素
} else if (index == 10) {
type = 2;
//中间普通元素
} else {
type = 1;
}
if (index < 5) {
colorType = Color(0xFF55D160);
} else if (index > 6) {
colorType = Color(0xFFFF2C2C);
} else {
colorType = Color(0xFFFFA82C);
}
return Container(
child: NumberPickerItem(
subGridCount: 10,
subGridWidth: 5,
itemWidth: widget.width.toInt(),
valueStr: ((10 - index) * 10).toString() + '分',
type: type,
colorType: colorType,
),
);
},
),
欢迎大家和我一起学习分享flutter,项目会持续更新新的学习demo
此项目的github地址:项目地址
下面是我们的公众号:flutter编程笔记(code9871)
公众号 不定期分享自己的学习想法
饮水思源,本demo参考了HorizontalNumberPicker项目。