自定义view-横向进度条
写作目的:QQ群有个兄弟在问这个控件如何实现,刚好前段时间学了自定义,现学现用写了个demo,也方便兄弟们可以学习一波自定义。这用到了绘制矩形,绘制圆形,手势。废话不多说。开干!
1.绘制矩形,并且展示圆弧角度。
///设置画笔,颜色,风格
final backgroundPaint = Paint()
..color = color2
..style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height),
Radius.circular(size.height / 2),
),
backgroundPaint,
);
- 继承CustomPainter,在paint 方法内设置第一个背景的画笔,颜色,风格。PaintingStyle.fill表示填充。
- 可以使用
drawRRect
方法来绘制圆角矩形(RRect
), 参数说明:
rrect
: 要绘制的圆角矩形对象,使用RRect
类创建。paint
: 绘制的样式和属性,使用Paint
类创建。- 然后使用
RRect.fromRectAndRadius
方法创建了一个圆角矩形rrect
Rect.fromLTWH
是创建矩形。
2.绘制第二个已完成的矩形,并且展示圆弧角度。
final background2Paint = Paint()
..color = color
..style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width * progress, size.height),
Radius.circular(size.height / 2),
),
background2Paint,
);
方法基本一样,只不过矩形区域的Rect.fromLTWH(0, 0, size.width * progress, size.height)第三个参数是动态获取的,progess表示总进度比例。
3.绘制进度条上的圆圈
final strokeWidth = 5.0;
final strokePaint = Paint()
..color = color
..strokeWidth = strokeWidth;
final centerX = size.width * progress;
final centerY = size.height / 2;
final radius = size.height / 2;
canvas.drawCircle(
Offset(centerX, centerY),
radius,
strokePaint..style = PaintingStyle.stroke,
);
canvas.drawCircle(
Offset(centerX, centerY),
radius,
strokePaint
..style = PaintingStyle.fill
..color = color2,
);
我实现的方式直接绘制了2个圆圈,内圆和外圆。
- 先初始化画笔的宽度
- 设置圆圈的半径,中心点的x,y点。
- 使用drawCircle方法绘制圆弧
- 分别设置
PaintingStyle.stroke
和PaintingStyle.fill
。
到目前为止 绘制的核心代码已经完毕。
如果有手势控制需求的。可以直接加上手势方法GestureDetector
,我们只需要监听横向事件的话增加onHorizontalDragUpdate
。方法内部需要动态的修改进度值,所以进度值需要做一些修改。这时候就可以讲一讲
CustomPainter与监听器。
CustomPainter 与监听器
这一点非常重要
,我是在看CupertinoActivityIndicator
组件源码时发现的这一点。因为 CustomPainter
的本身是一个Listenable
子类,可以传入一个Listenable对象
, 这个对象进行更新时,会触发通知让 CustomPainter
进行重绘。就不需要
使用组件状态的 setState
来完成画布的刷新。这点在 CustomPainter
的源码中也有明确指出:
最高效地触发画板重绘的方式是:
[1]. 继承自 CustomPainter,在构造函数中对父类 repaint属性 进行赋值,repaint是一个可监听对象,当对象变化时会触发画布的重绘。 [2]. 继承自 Listenable 实现 CustomPainter,让该类自己执行对自己的更新。
经过修改之后的源码:
import 'package:flutter/material.dart';
class CustomProgressBar extends StatefulWidget {
final double progress;
final Color color;
final Color color2;
final bool enableGesture;
final double width;
final double height;
CustomProgressBar({
required this.progress,
required this.color,
required this.color2,
this.enableGesture = false,
this.width = 200,
this.height = 20,
});
@override
_CustomProgressBarState createState() => _CustomProgressBarState();
}
class _CustomProgressBarState extends State<CustomProgressBar> {
ValueNotifier<double> _currentProgress = ValueNotifier(0.0);
double progress = 0;
@override
void initState() {
super.initState();
_currentProgress.value = widget.progress;
}
void _updateProgress(DragUpdateDetails details) {
progress += details.delta.dx / context.size!.width;
progress = progress.clamp(0.0, 1.0);
_currentProgress.value = progress;
}
@override
Widget build(BuildContext context) {
return
Container(
height: widget.height,
width: widget.width,
child: widget.enableGesture
? GestureDetector(
onHorizontalDragUpdate: _updateProgress,
child: _buildProgressBar(),
)
: _buildProgressBar(),
);
}
Widget _buildProgressBar() {
return CustomPaint(
painter: ProgressBarPainter(
progress: _currentProgress,
color: widget.color,
color2: widget.color2,
),
);
}
}
class ProgressBarPainter extends CustomPainter {
final ValueNotifier<double> progress;// <--- 定义成员变量
final Color color;
final Color color2;
ProgressBarPainter({
required this.progress,
required this.color,
required this.color2,
}):super(repaint: progress);// <--- 传入 Listenable 可监听对象
@override
void paint(Canvas canvas, Size size) {
///设置画笔,颜色,风格
final backgroundPaint = Paint()
..color = color2
..style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height),
Radius.circular(size.height / 2),
),
backgroundPaint,
);
final background2Paint = Paint()
..color = color
..style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width * progress.value, size.height),
Radius.circular(size.height / 2),
),
background2Paint,
);
final strokeWidth = 5.0;
final strokePaint = Paint()
..color = color
..strokeWidth = strokeWidth;
final centerX = size.width * progress.value;
final centerY = size.height / 2;
final radius = size.height / 2;
canvas.drawCircle(
Offset(centerX, centerY),
radius,
strokePaint..style = PaintingStyle.stroke,
);
canvas.drawCircle(
Offset(centerX, centerY),
radius,
strokePaint
..style = PaintingStyle.fill
..color = color2,
);
}
@override
bool shouldRepaint(ProgressBarPainter oldDelegate) {
return oldDelegate.progress != progress ||
oldDelegate.color != color ||
oldDelegate.color2 != color2;
}
}
使用:
CustomProgressBar(progress: 0.7,color: Colors.white,color2:Colors.blue.shade200,enableGesture: true,)
这这是抛砖引玉,自己还可以根据上面的代码扩展完善,增加渐变,动画,刻度。。。。 自定义系统学习传送门:s.juejin.cn/ds/imEMWF7/
感兴趣可以加qq群882974689交流flutter。