代码语言:javascript复制今天写个简单的,自定义一个圆形进度条,并且加上小箭头指向内圈进度。 进度条已上传到公网,使用
circle_progress: ^0.0.1
,使用如下
void main() => runApp(MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text("Flutter 之旅"),
),
body: TestStateful() //内置案例
)
));
1.准备阶段
1.1:定义描述对象类Progress
代码语言:javascript复制将需要变化的属性抽离出一个描述类,传参方便些
///信息描述类 [value]为进度,在0~1之间,进度条颜色[color],
///未完成的颜色[backgroundColor],圆的半径[radius],线宽[strokeWidth]
///小点的个数[dotCount] 样式[style] 完成后的显示文字[completeText]
class Progress {
double value;
Color color;
Color backgroundColor;
double radius;
double strokeWidth;
int dotCount;
TextStyle style;
String completeText;
Progress({this.value,
this.color,
this.backgroundColor,
this.radius,
this.strokeWidth,
this.completeText="OK",
this.style,
this.dotCount = 40
});
}
1.2:自定义组件类CircleProgressWidget
代码语言:javascript复制这便是我们的组件
class CircleProgressWidget extends StatefulWidget {
final Progress progress;
CircleProgressWidget({Key key, this.progress}) : super(key: key);
@override
_CircleProgressWidgetState createState() => _CircleProgressWidgetState();
}
class _CircleProgressWidgetState extends State {
@override
Widget build(BuildContext context) {
return Container();
}
}
1.3:自定义ProgressPainter
代码语言:javascript复制我们的绘制逻辑在这里进行
class ProgressPainter extends CustomPainter {
Progress _progress;
Paint _paint;
Paint _arrowPaint;//箭头的画笔
Path _arrowPath;//箭头的路径
double _radius;//半径
ProgressPainter(
this._progress,
) {
_arrowPath=Path();
_arrowPaint=Paint();
_paint = Paint();
_radius = _progress.radius - _progress.strokeWidth / 2;
}
@override
void paint(Canvas canvas, Size size) {
Rect rect = Offset.zero & size;
canvas.clipRect(rect); //裁剪区域
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
2.绘制
2.1:绘制进度条
代码语言:javascript复制如果直接用给定的半径,你会发现是这样的。原因很简单,因为Canvas画圆半径是内圆加一半线粗。 于是我们需要校正一下半径:通过平移一半线粗再缩小一半线粗的半径。
_radius = _progress.radius - _progress.strokeWidth / 2;
@override
void paint(Canvas canvas, Size size) {
canvas.translate(_progress.strokeWidth / 2, _progress.strokeWidth / 2);
代码语言:javascript复制背景直接画圆,进度使用drawArc方法,要注意的是Flutter中使用的是
弧度
!!。
drawProgress(Canvas canvas) {
canvas.save();
_paint//背景
..style = PaintingStyle.stroke
..color = _progress.backgroundColor
..strokeWidth = _progress.strokeWidth;
canvas.drawCircle(Offset(_radius, _radius), _radius, _paint);
_paint//进度
..color = _progress.color
..strokeWidth = _progress.strokeWidth * 1.2
..strokeCap = StrokeCap.round;
double sweepAngle = _progress.value * 360; //完成角度
canvas.drawArc(Rect.fromLTRB(0, 0, _radius * 2, _radius * 2),
-90 / 180 * pi, sweepAngle / 180 * pi, false, _paint);
canvas.restore();
}
2.2:绘制箭头
代码语言:javascript复制其实箭头还是蛮好画的,注意relativeLineTo和lineTo结合使用,可能会更方便。
drawArrow(Canvas canvas) {
canvas.save();
canvas.translate(_radius, _radius);
canvas.rotate((180 _progress.value * 360) / 180 * pi);
var half = _radius / 2;
var eg = _radius / 50; //单位长
_arrowPath.moveTo(0, -half - eg * 2);//1
_arrowPath.relativeLineTo(eg * 2, eg * 6);//2
_arrowPath.lineTo(0, -half eg * 2);//3
_arrowPath.lineTo(0, -half - eg * 2);//1
_arrowPath.relativeLineTo(-eg * 2, eg * 6);
_arrowPath.lineTo(0, -half eg * 2);
_arrowPath.lineTo(0, -half - eg * 2);
canvas.drawPath(_arrowPath, _arrowPaint);
canvas.restore();
}
2.3:绘制点
代码语言:javascript复制绘制点的时候要注意颜色的把控,判断进度条是否到达,然后更改颜色
void drawDot(Canvas canvas) {
canvas.save();
int num = _progress.dotCount;
canvas.translate(_radius, _radius);
for (double i = 0; i < num; i ) {
canvas.save();
double deg = 360 / num * i;
canvas.rotate(deg / 180 * pi);
_paint
..strokeWidth = _progress.strokeWidth / 2
..color = _progress.backgroundColor
..strokeCap = StrokeCap.round;
if (i * (360 / num) <= _progress.value * 360) {
_paint..color = _progress.color;
}
canvas.drawLine(
Offset(0, _radius * 3 / 4), Offset(0, _radius * 4 / 5), _paint);
canvas.restore();
}
canvas.restore();
}
2.4:拼装
代码语言:javascript复制也许你会问Text呢?在Canvas里画Text好麻烦,这里用一个Stack包一下挺方便的
class _CircleProgressWidgetState extends State {
@override
Widget build(BuildContext context) {
var progress = Container(
width: widget.progress.radius * 2,
height: widget.progress.radius * 2,
child: CustomPaint(
painter: ProgressPainter(widget.progress),
),
);
String txt = "${(100 * widget.progress.value).toStringAsFixed(1)} %";
var text = Text(
widget.progress.value == 1.0 ? widget.progress.completeText : txt,
style: widget.progress.style ??
TextStyle(fontSize: widget.progress.radius / 6),
);
return Stack(
alignment: Alignment.center,
children: [progress,text],
);
}
}
OK,这样就可以了。
3.使用
3.1:简单使用
代码语言:javascript复制CircleProgressWidget(
progress: Progress(
backgroundColor: Colors.grey,
value: 0.8,
radius: 100,
completeText: "完成",
color: Color(0xff46bcf6),
strokeWidth: 4))
3.2:组件代码全览
代码语言:javascript复制拿去用吧,谢谢,不送。
import 'dart:math';
import 'package:flutter/material.dart';
class CircleProgressWidget extends StatefulWidget {
final Progress progress;
CircleProgressWidget({Key key, this.progress}) : super(key: key);
@override
_CircleProgressWidgetState createState() => _CircleProgressWidgetState();
}
///信息描述类 [value]为进度,在0~1之间,进度条颜色[color],
///未完成的颜色[backgroundColor],圆的半径[radius],线宽[strokeWidth]
///小点的个数[dotCount] 样式[style] 完成后的显示文字[completeText]
class Progress {
double value;
Color color;
Color backgroundColor;
double radius;
double strokeWidth;
int dotCount;
TextStyle style;
String completeText;
Progress(
{this.value,
this.color,
this.backgroundColor,
this.radius,
this.strokeWidth,
this.completeText = "OK",
this.style,
this.dotCount = 40});
}
class _CircleProgressWidgetState extends State {
@override
Widget build(BuildContext context) {
var progress = Container(
width: widget.progress.radius * 2,
height: widget.progress.radius * 2,
child: CustomPaint(
painter: ProgressPainter(widget.progress),
),
);
String txt = "${(100 * widget.progress.value).toStringAsFixed(1)} %";
var text = Text(
widget.progress.value == 1.0 ? widget.progress.completeText : txt,
style: widget.progress.style ??
TextStyle(fontSize: widget.progress.radius / 6),
);
return Stack(
alignment: Alignment.center,
children: [progress,text],
);
}
}
class ProgressPainter extends CustomPainter {
Progress _progress;
Paint _paint;
Paint _arrowPaint;
Path _arrowPath;
double _radius;
ProgressPainter(
this._progress,
) {
_arrowPath = Path();
_arrowPaint = Paint();
_paint = Paint();
_radius = _progress.radius - _progress.strokeWidth / 2;
}
@override
void paint(Canvas canvas, Size size) {
Rect rect = Offset.zero & size;
canvas.clipRect(rect); //裁剪区域
canvas.translate(_progress.strokeWidth / 2, _progress.strokeWidth / 2);
drawProgress(canvas);
drawArrow(canvas);
drawDot(canvas);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
drawProgress(Canvas canvas) {
canvas.save();
_paint//背景
..style = PaintingStyle.stroke
..color = _progress.backgroundColor
..strokeWidth = _progress.strokeWidth;
canvas.drawCircle(Offset(_radius, _radius), _radius, _paint);
_paint//进度
..color = _progress.color
..strokeWidth = _progress.strokeWidth * 1.2
..strokeCap = StrokeCap.round;
double sweepAngle = _progress.value * 360; //完成角度
print(sweepAngle);
canvas.drawArc(Rect.fromLTRB(0, 0, _radius * 2, _radius * 2),
-90 / 180 * pi, sweepAngle / 180 * pi, false, _paint);
canvas.restore();
}
drawArrow(Canvas canvas) {
canvas.save();
canvas.translate(_radius, _radius);// 将画板移到中心
canvas.rotate((180 _progress.value * 360) / 180 * pi);//旋转相应角度
var half = _radius / 2;//基点
var eg = _radius / 50; //单位长
_arrowPath.moveTo(0, -half - eg * 2);
_arrowPath.relativeLineTo(eg * 2, eg * 6);
_arrowPath.lineTo(0, -half eg * 2);
_arrowPath.lineTo(0, -half - eg * 2);
_arrowPath.relativeLineTo(-eg * 2, eg * 6);
_arrowPath.lineTo(0, -half eg * 2);
_arrowPath.lineTo(0, -half - eg * 2);
canvas.drawPath(_arrowPath, _arrowPaint);
canvas.restore();
}
void drawDot(Canvas canvas) {
canvas.save();
int num = _progress.dotCount;
canvas.translate(_radius, _radius);
for (double i = 0; i < num; i ) {
canvas.save();
double deg = 360 / num * i;
canvas.rotate(deg / 180 * pi);
_paint
..strokeWidth = _progress.strokeWidth / 2
..color = _progress.backgroundColor
..strokeCap = StrokeCap.round;
if (i * (360 / num) <= _progress.value * 360) {
_paint..color = _progress.color;
}
canvas.drawLine(
Offset(0, _radius * 3 / 4), Offset(0, _radius * 4 / 5), _paint);
canvas.restore();
}
canvas.restore();
}
}