原文是谁写的我也不知道,但是搜了这么多,没一个能用的,大多数的都没实时刷新,在自己的debug和学习下,进行了改进,这次可以直接用了xdm

学习的笔记也记录在这里了

绘制需要的要素
1.纸: Canvas 画布对象
2.笔: Paint 画笔对象
3.形: Path 路径对象
4.色: Color 颜色对象

使用StreamController来进行实时的局部刷新

效果图:

程序主入口

void main() => runApp(MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: Scaffold(appBar: AppBar(title: Text("Flutter 之旅"),),body: CircleProgressWidget(progress: Progress(backgroundColor: Colors.grey,value: 0.0,radius: 100,completeText: "完成",color: Color(0xff46bcf6),strokeWidth: 4)), )
));

1.定义描述对象类Progress

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自定义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;}@overridevoid paint(Canvas canvas, Size size) { //这个是我们定义painter时必须实现的方法,其中canvas就是提供出我们绘制的核心,//size是告诉我们画板的大小(通过CustomPaint的size或者child确定)Rect rect = Offset.zero & size;canvas.clipRect(rect); //裁剪区域}@overridebool shouldRepaint(CustomPainter oldDelegate) {return true;}
}

2.绘制

2.1:绘制进度条

如果直接用给定的半径,你会发现是这样的。原因很简单,因为Canvas画圆半径是内圆加一半线粗。
于是我们需要校正一下半径:通过平移一半线粗再缩小一半线粗的半径。

_radius = _progress.radius - _progress.strokeWidth / 2;@overridevoid paint(Canvas canvas, Size size) {canvas.translate(_progress.strokeWidth / 2, _progress.strokeWidth / 2);


背景直接画圆,进度使用drawArc方法,要注意的是Flutter中使用的是弧度!!。

save操作会保存此前的所有绘制内容和canvas状态。在调用该函数之后的绘制操作和变换操作,会重新记录。当你调用restore()之后,会把save到restore之间所进行的操作与之前的内容进行合并,之后再使用局部更新的StreamController来更新状态

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();
}

3.组装使用

使用_streamController来更新进度条状态

class CircleProgressWidget extends StatefulWidget {final Progress progress;CircleProgressWidget({Key key, this.progress}) : super(key: key);@override_CircleProgressWidgetState createState() => _CircleProgressWidgetState(this.progress);
}class _CircleProgressWidgetState extends State<CircleProgressWidget> {Progress progress;_CircleProgressWidgetState(this.progress);///计时器Timer _timer;///倒计时6秒double totalTimeNumber = 10000;///当前的时间double currentTimeNumber = 10000;StreamController<double> _streamController = StreamController();@overridevoid initState(){startTimer();}@overridevoid dispose(){_streamController.close();_timer.cancel();}void startTimer() {///间隔100毫秒执行时间_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {///间隔100毫秒执行一次 每次减100currentTimeNumber -= 100;///如果计完成取消定时if (currentTimeNumber <= 0) {_timer.cancel();currentTimeNumber = 0;}///流数据更新progress.value = (totalTimeNumber-currentTimeNumber)/totalTimeNumber;_streamController.add((totalTimeNumber-currentTimeNumber)/totalTimeNumber);});}@overrideWidget 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 Scaffold(body: Container(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height, //容器填充满整个屏幕child: StreamBuilder<double>(stream: _streamController.stream,initialData: 0,builder: (BuildContext context, AsyncSnapshot<double> snapshot) {return Stack(alignment: Alignment.center,children: [Text(widget.progress.value == 1.0 ? widget.progress.completeText : "${(100 * widget.progress.value).toStringAsFixed(1)} %",style: widget.progress.style ??TextStyle(fontSize: widget.progress.radius / 6),),Container(width: widget.progress.radius * 2,height: widget.progress.radius * 2,child: CustomPaint(painter: ProgressPainter(widget.progress),),),],);},),));}
}

4.全部代码

import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';///信息描述类 [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 CircleProgressWidget extends StatefulWidget {final Progress progress;CircleProgressWidget({Key key, this.progress}) : super(key: key);@override_CircleProgressWidgetState createState() => _CircleProgressWidgetState(this.progress);
}class _CircleProgressWidgetState extends State<CircleProgressWidget> {Progress progress;_CircleProgressWidgetState(this.progress);///计时器Timer _timer;///倒计时6秒double totalTimeNumber = 10000;///当前的时间double currentTimeNumber = 10000;StreamController<double> _streamController = StreamController();@overridevoid initState(){startTimer();}@overridevoid dispose(){_streamController.close();_timer.cancel();}void startTimer() {///间隔100毫秒执行时间_timer = Timer.periodic(Duration(milliseconds: 100), (timer) {///间隔100毫秒执行一次 每次减100currentTimeNumber -= 100;///如果计完成取消定时if (currentTimeNumber <= 0) {_timer.cancel();currentTimeNumber = 0;}///流数据更新progress.value = (totalTimeNumber-currentTimeNumber)/totalTimeNumber;_streamController.add((totalTimeNumber-currentTimeNumber)/totalTimeNumber);});}@overrideWidget 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 Scaffold(body: Container(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height, //容器填充满整个屏幕child: StreamBuilder<double>(stream: _streamController.stream,initialData: 0,builder: (BuildContext context, AsyncSnapshot<double> snapshot) {return Stack(alignment: Alignment.center,children: [Text(widget.progress.value == 1.0 ? widget.progress.completeText : "${(100 * widget.progress.value).toStringAsFixed(1)} %",style: widget.progress.style ??TextStyle(fontSize: widget.progress.radius / 6),),Container(width: widget.progress.radius * 2,height: widget.progress.radius * 2,child: CustomPaint(painter: ProgressPainter(widget.progress),),),],);},),));}
}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;}@overridevoid 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);}@overridebool 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();}
}

参考1

flutter 自定义圆形进度条相关推荐

  1. Android自定义圆形进度条

    Android自定义圆形进度条 github地址:https://github.com/opq1289/CircleProgressView 效果图: 无动画: 有动画: 整圆: 切割圆: 具体步骤: ...

  2. android绘制环形进度_Android动态自定义圆形进度条

    这篇文章主要介绍了Android动态自定义圆形进度条,需要的朋友可以参考下 效果图: A.绘制圆环,圆弧,文本 //1.画圆环 //原点坐标 float circleX = width / 2; fl ...

  3. 微信小程序进度条样式_详解微信小程序——自定义圆形进度条

    微信小程序 自定义圆形进度条,具体如下: 无图无真相,先上图: 实现思路,先绘制底层的灰色圆圈背景,再绘制上层的蓝色进度条. 代码实现: JS代码: 页面初始化 options为页面跳转所带来的参数 ...

  4. 自定义圆形进度条的实现方式

    如何自定义圆形进度条哪,也就是替换一下进度条的图片而已. 先分析一下,系统对进度条如何定义的: 咱们一般情况下载布局文件中这么书写: //在布局文件里的代码<ProgressBarandroid ...

  5. 自定义圆形进度条 自定义倒计时进度条

    自定义圆形进度条 自定义倒计时进度条 版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003 此控件源码已开源到Github:https: ...

  6. android自定义圆形进度条,实现动态画圆效果

    自定义圆形进度条效果图如下:应用场景如动态显示分数等. view的自定义属性如下attr.xml <?xml version="1.0" encoding="UTF ...

  7. QT自定义圆形进度条

    以下是一个简单的示例,展示如何创建一个自定义的圆形进度条控件. 1.创建一个新的Qt控件类,继承QProgressBar类.在该类的头文件中添加以下代码:     class CircularProg ...

  8. android 环形时间显示_Android_Android实现自定义圆形进度条,今天无意中发现一个圆形进度 - phpStudy...

    Android实现自定义圆形进度条 今天无意中发现一个圆形进度,想想自己实现一个,如下图: 基本思路是这样的: 1.首先绘制一个实心圆 2.绘制一个白色实心的正方形,遮住实心圆 3.在圆的中心动态绘制 ...

  9. 【Android 应用开发】 自定义 圆形进度条 组件

    转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://down ...

最新文章

  1. JVM 史上最最最完整深入解析(12000 字噢)
  2. C++ stack容器
  3. Python 的闭包和装饰器
  4. mysql128位整数_mysql的数据类型
  5. 使用 Xunit.DependencyInjection 改造测试项目
  6. 二叉搜索时与双向链表python_剑指Offer(二十六) 二叉搜索树和双向链表
  7. 【CodeForces - 122B 】Lucky Substring (字符串,水题)
  8. 云服务器 性能监控软件,云监控 - 云应用监控 - ManageEngine Applications Manager
  9. 微信重大更新,mac版可刷朋友圈!可以看,可以评论! 支持M1
  10. 【ambari】Ambari Rest api 使用
  11. 安卓基础干货(四):安卓网络编程的学习
  12. OA审批工作流设计器实现
  13. 一键查询网站服务器归属地,一文搞定3种批量查询手机归属地的方法
  14. STK对卫星轨道摄动的处理
  15. 撞线百亿后,良品铺子峥嵘毕现?
  16. 中国古语中的十大智慧
  17. Android画正N边形战力图
  18. springboot基于java的校园二手书籍交易平台毕业设计源码131558
  19. Blast中文手册(6)
  20. 通往测试架构师之路(1):那些家伙在干什么?

热门文章

  1. 博主文章导航(分门别类,实时更新,永久置顶)
  2. 影像组学|特征定义以及提取
  3. 拼多多“百亿农研”开辟发展新路径
  4. oracle无法打开12560
  5. RSPSS重复测量方差分析
  6. java 实现输出水仙花数
  7. 运维人员必会的系统安全
  8. python网络爬虫从入门到实践 第5章 (二)
  9. 计算机视觉需要学习哪些编程语言?
  10. mysql中对数据的去重