[-Flutter 自组篇-] 圆形进度条
时间:2022-07-27
本文章向大家介绍[-Flutter 自组篇-] 圆形进度条,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
今天写个简单的,自定义一个圆形进度条,并且加上小箭头指向内圈进度。 进度条已上传到公网,使用
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
将需要变化的属性抽离出一个描述类,传参方便些
///信息描述类 [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
这便是我们的组件
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
我们的绘制逻辑在这里进行
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:绘制进度条
如果直接用给定的半径,你会发现是这样的。原因很简单,因为Canvas画圆半径是内圆加一半线粗。 于是我们需要校正一下半径:通过平移一半线粗再缩小一半线粗的半径。
_radius = _progress.radius - _progress.strokeWidth / 2;
@override
void paint(Canvas canvas, Size size) {
canvas.translate(_progress.strokeWidth / 2, _progress.strokeWidth / 2);
背景直接画圆,进度使用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:绘制箭头
其实箭头还是蛮好画的,注意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:绘制点
绘制点的时候要注意颜色的把控,判断进度条是否到达,然后更改颜色
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:拼装
也许你会问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:简单使用
CircleProgressWidget(
progress: Progress(
backgroundColor: Colors.grey,
value: 0.8,
radius: 100,
completeText: "完成",
color: Color(0xff46bcf6),
strokeWidth: 4))
3.2:组件代码全览
拿去用吧,谢谢,不送。
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();
}
}
- hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)
- HDU 2546 饭卡(01背包裸题)
- 漫谈文件系统
- AI知识搜索利器:基于ElasticSearch构建专知实时高性能搜索系统
- 【深度干货】专知主题链路知识推荐#5-机器学习中似懂非懂的马尔科夫链蒙特卡洛采样(MCMC)入门教程01
- hihoCoder #1043 : 完全背包(板子题)
- 【深度干货】专知主题链路知识推荐#7-机器学习中似懂非懂的马尔科夫链蒙特卡洛采样(MCMC)入门教程02
- hihoCoder #1038 : 01背包(板子题)
- 最小二乘法多项式曲线拟合原理与实现
- HDU 1166 敌兵布阵(线段树单点更新,板子题)
- 一文看懂ovirt的supervdsmd服务
- openstack如何扩展API之二:扩展原有核心API
- selenium+python自动化77-autoit文件上传
- selenium+python自动化78-autoit参数化与批量上传
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 短信收发类无错版JustinIO.cs
- Android快速实现无预览拍照功能
- RecyclerView+PagerSnapHelper实现抖音首页翻页的Viewpager效果
- android中使用react-native设置应用启动页过程详解
- 面试官问我单例模式真的安全吗?我懵逼了
- 详解Android使用CoordinatorLayout+AppBarLayout实现拉伸顶部图片功能
- Android自定义控制条效果
- Android使用MediaPlayer和TextureView实现视频无缝切换
- Android实现静默拍照功能
- Android实现动态体温计
- Android实现倾斜角标样式
- 浅谈Flutter 中渐变的高级用法(3种)
- Android实现左上角(其他边角)倾斜的标签(环绕效果)效果
- Android开发中Button组件的使用
- Android开发之基于RecycleView实现的头部悬浮控件