首先我们定义一个椭圆弧型进度条组件。
/// 圆弧进度条
class CurveProgressIndicator extends StatelessWidget {
const CurveProgressIndicator({
super.key,
this.size = Size.zero,
required this.progress,
});
/// 监听器
final ValueNotifier<double> progress;
/// 尺寸
final Size size;
@override
Widget build(BuildContext context) {
debugPrint("重绘:底部进度条");
return RepaintBoundary(
child: CustomPaint(
size: size,
painter: CurveProgressIndicatorPainter(progress),
),
);
}
}
接着我们定义一个自定义画刷。
/// 圆弧进度条的绘制器
class CurveProgressIndicatorPainter extends CustomPainter {
const CurveProgressIndicatorPainter(this.progress) : super(repaint: progress);
/// 监听器
final ValueNotifier<double> progress;
@override
void paint(Canvas canvas, Size size) {
if (progress.value == 0) {
//没有进度不绘制
return;
}
debugPrint("重绘:底部进度条-画刷");
// 贝塞尔曲率
double bezier = size.height / 4;
// 绘制弧线
Path line = Path()
..moveTo(0, bezier)
..quadraticBezierTo(size.width / 2, -bezier, size.width, bezier);
// 定义画刷
var gradient = Gradient.linear(
Offset.zero,
Offset(size.width, 0),
[
const Color(0xFFB7EAFF),
const Color(0xFF7ED9FF),
const Color(0xFF0F7BAA),
// Colors.red, Colors.blue, Colors.yellow,
],
[0, 0.6, 1],
);
// 绘制扇形进度条
var paint = Paint()
//..color = progressColor
..shader = gradient
..strokeWidth = 2.5
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
//计算百分比进度条
PathMetrics pathMetrics = line.computeMetrics();
// 获取第一小节信息(猜测可能有多个Path叠加?)
PathMetric pathMetric = pathMetrics.first;
// 整个Path的长度
double length = pathMetric.length;
// 当前进度
double value = length * progress.value;
Path extractPath = pathMetric.extractPath(0, value, startWithMoveTo: true);
canvas.drawPath(extractPath, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
详细解析
弧线的路径定义:
定义一个高度为Size容器 4分之1的高度 (我这里是将Size设置成100,因为我的案例中播放器底部有一个圆弧形的背景, 可以根据实际情况设置参数),
将起点放置于(0,bezier),绘制弧线点(x1,y1) = (宽度/2,-bezier) ,(x2,y3) = (宽度,bezier)。
关于百分比进度的绘制方法:
需要根据PathMetrics获取到line的总长度,再通过 double value = length * progress.value 计算出当前的路径,最后绘制 渐变色 就完成了。
//计算百分比进度条
PathMetrics pathMetrics = line.computeMetrics();
// 获取第一小节信息(猜测可能有多个Path叠加?)
PathMetric pathMetric = pathMetrics.first;
// 整个Path的长度
double length = pathMetric.length;
// 当前进度
double value = length * progress.value;
Path extractPath = pathMetric.extractPath(0, value, startWithMoveTo: true);
canvas.drawPath(extractPath, paint);
看看成品:
整个app的源码地址:
在线体验效果(服务器带宽比较低):
2024-11-18 更新: 1.优化 路径绘制方案,修改为Slider方式,可以支持点击和拖拽。 2.增加几首歌单 3.播放列表切换歌曲
Path line = Path()
..moveTo(0, size.height)
..quadraticBezierTo(
size.width / 2,
-size.height,
size.width,
size.height,
);
//计算百分比进度条
ui.PathMetrics pathMetrics = line.computeMetrics();
// 获取第一小节信息(猜测可能有多个Path叠加?)
ui.PathMetric pathMetric = pathMetrics.first;
// 整个Path的长度
double length = pathMetric.length;
// 当前进度
double value = length * controllerValue;
// 左侧路径
Path extractLeftPath =
pathMetric.extractPath(0, value, startWithMoveTo: true);
//绘制第1段颜色
context.canvas.drawPath(extractLeftPath, activePaint);
// 获取滑块绘制的位置
ui.Tangent? tangent = pathMetric.getTangentForOffset(value);
/// 滑块在路径上
thumbCenter = tangent!.position;