https://www.jianshu.com/p/2ea01ae02ffe

Flutter:教你用CustomPaint画一个自定义的CircleProgressBar

paint_page.dart

import 'package:flutter/material.dart';
import 'package:tetris/paint/paint.dart'; class ProgressBarPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => ProgressBarState();
} class ProgressBarState extends State<ProgressBarPage> {
double progress = 0.0; @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CircleProgressBar'),
),
body: Stack(
alignment: Alignment.center,
children: <Widget>[
Text('Degree:${(progress * 360.0).round()}°',style: TextStyle(fontSize: 20.0),),
CircleProgressBar(
radius: 120.0,
dotColor: Colors.pink,
dotRadius: 18.0,
shadowWidth: 2.0,
progress: 0.0,
progressChanged: (value) {
setState(() {
progress = value;
});
},
),
],
),
);
}
}

  

paint.dart

import 'dart:math';

import 'package:flutter/material.dart';

typedef ProgressChanged<double> = void Function(double value);

num degToRad(num deg) => deg * (pi / 180.0);

num radToDeg(num rad) => rad * (180.0 / pi);

class CircleProgressBar extends StatefulWidget {
final double radius;
final double progress;
final double dotRadius;
final double shadowWidth;
final Color shadowColor;
final Color dotColor;
final Color dotEdgeColor;
final Color ringColor; final ProgressChanged progressChanged; const CircleProgressBar({
Key key,
@required this.radius,
@required this.dotRadius,
@required this.dotColor,
this.shadowWidth = 2.0,
this.shadowColor = Colors.black12,
this.ringColor = const Color(0XFFF7F7FC),
this.dotEdgeColor = const Color(0XFFF5F5FA),
this.progress,
this.progressChanged,
}) : super(key: key); @override
State<StatefulWidget> createState() => _CircleProgressState();
} class _CircleProgressState extends State<CircleProgressBar>
with SingleTickerProviderStateMixin {
AnimationController progressController;
bool isValidTouch = false;
final GlobalKey paintKey = GlobalKey(); @override
void initState() {
super.initState();
progressController =
AnimationController(duration: Duration(milliseconds: 300), vsync: this);
if (widget.progress != null) progressController.value = widget.progress;
progressController.addListener(() {
if (widget.progressChanged != null)
widget.progressChanged(progressController.value);
setState(() {});
});
} @override
void dispose() {
progressController.dispose();
super.dispose();
} @override
Widget build(BuildContext context) {
final double width = widget.radius * 2.0;
final size = new Size(width, width);
return GestureDetector(
onPanStart: _onPanStart,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: Container(
alignment: FractionalOffset.center,
child: CustomPaint(
key: paintKey,
size: size,
painter: ProgressPainter(
dotRadius: widget.dotRadius,
shadowWidth: widget.shadowWidth,
shadowColor: widget.shadowColor,
ringColor: widget.ringColor,
dotColor: widget.dotColor,
dotEdgeColor: widget.dotEdgeColor,
progress: progressController.value),
),
),
);
} void _onPanStart(DragStartDetails details) {
RenderBox getBox = paintKey.currentContext.findRenderObject();
Offset local = getBox.globalToLocal(details.globalPosition);
isValidTouch = _checkValidTouch(local);
if (!isValidTouch) {
return;
}
} void _onPanUpdate(DragUpdateDetails details) {
if (!isValidTouch) {
return;
}
RenderBox getBox = paintKey.currentContext.findRenderObject();
Offset local = getBox.globalToLocal(details.globalPosition);
final double x = local.dx;
final double y = local.dy;
final double center = widget.radius;
double radians = atan((x - center) / (center - y));
if (y > center) {
radians = radians + degToRad(180.0);
} else if (x < center) {
radians = radians + degToRad(360.0);
}
progressController.value = radians / degToRad(360.0);
} void _onPanEnd(DragEndDetails details) {
if (!isValidTouch) {
return;
}
} bool _checkValidTouch(Offset pointer) {
final double validInnerRadius = widget.radius - widget.dotRadius * 3;
final double dx = pointer.dx;
final double dy = pointer.dy;
final double distanceToCenter =
sqrt(pow(dx - widget.radius, 2) + pow(dy - widget.radius, 2));
if (distanceToCenter < validInnerRadius ||
distanceToCenter > widget.radius) {
return false;
}
return true;
}
} class ProgressPainter extends CustomPainter {
final double dotRadius;
final double shadowWidth;
final Color shadowColor;
final Color dotColor;
final Color dotEdgeColor;
final Color ringColor;
final double progress; ProgressPainter({
this.dotRadius,
this.shadowWidth = 2.0,
this.shadowColor = Colors.black12,
this.ringColor = const Color(0XFFF7F7FC),
this.dotColor,
this.dotEdgeColor = const Color(0XFFF5F5FA),
this.progress,
}); @override
void paint(Canvas canvas, Size size) {
final double center = size.width * 0.5;
final Offset offsetCenter = Offset(center, center);
final double drawRadius = size.width * 0.5 - dotRadius;
final angle = 360.0 * progress;
final double radians = degToRad(angle); final double radiusOffset = dotRadius * 0.4;
final double outerRadius = center - radiusOffset;
final double innerRadius = center - dotRadius * 2 + radiusOffset; // draw shadow.
final shadowPaint = Paint()
..style = PaintingStyle.stroke
..color = shadowColor
..strokeWidth = shadowWidth
..maskFilter = MaskFilter.blur(BlurStyle.normal, shadowWidth);
// canvas.drawCircle(offsetCenter, outerRadius, shadowPaint);
// canvas.drawCircle(offsetCenter, innerRadius, shadowPaint); Path path = Path.combine(PathOperation.difference, Path()..addOval(Rect.fromCircle(center: offsetCenter, radius: outerRadius)), Path()..addOval(Rect.fromCircle(center: offsetCenter, radius: innerRadius)));
canvas.drawShadow(path, shadowColor, 4.0, true); // draw ring.
final ringPaint = Paint()
..style = PaintingStyle.stroke
..color = ringColor
..strokeWidth = (outerRadius - innerRadius);
canvas.drawCircle(offsetCenter, drawRadius, ringPaint); final Color currentDotColor = Color.alphaBlend(
dotColor.withOpacity(0.7 + (0.3 * progress)), Colors.white); // draw progress.
if (progress > 0.0) {
final progressWidth = outerRadius - innerRadius + radiusOffset;
final double offset = asin(progressWidth * 0.5 / drawRadius);
if (radians > offset) {
canvas.save();
canvas.translate(0.0, size.width);
canvas.rotate(degToRad(-90.0));
final Gradient gradient = new SweepGradient(
endAngle: radians,
colors: [
Colors.white,
currentDotColor,
],
);
final Rect arcRect =
Rect.fromCircle(center: offsetCenter, radius: drawRadius);
final progressPaint = Paint()
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round
..strokeWidth = progressWidth
..shader = gradient.createShader(arcRect);
canvas.drawArc(
arcRect, offset, radians - offset, false, progressPaint);
canvas.restore();
}
} // draw dot.
final double dx = center + drawRadius * sin(radians);
final double dy = center - drawRadius * cos(radians);
final dotPaint = Paint()..color = currentDotColor;
canvas.drawCircle(new Offset(dx, dy), dotRadius, dotPaint);
dotPaint
..color = dotEdgeColor
..style = PaintingStyle.stroke
..strokeWidth = dotRadius * 0.3;
canvas.drawCircle(new Offset(dx, dy), dotRadius, dotPaint); // canvas.drawLine(
// Offset(center, 0.0),
// Offset(center, size.height),
// Paint()
// ..strokeWidth = 1.0
// ..color = Colors.black); // 测试基准线
} @override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

  

Flutter:教你用CustomPaint画一个自定义的CircleProgressBar的更多相关文章

  1. 使用CAShapeLayer和UIBezierPath画一个自定义半圆弧button

    通常我们使用系统自带的UIButton时,一般都是Rect矩形形式的,或则美工给出一张半圆弧的按钮,如图为一张半圆加三角形的按钮,而此时,如果给按钮添加点击事件时,响应事件依然为矩形区域,不符合我们的 ...

  2. 手对手的教你用canvas画一个简单的海报

    啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信.qq等平台.图片中包含用户的姓名.头像.和带着自己信息的二维码.然后,如何生成这张海报呢~~~首先我们老大告诉我有一个插件叫htm ...

  3. 用C语言画一个心

    用C语言图形库画一个心 --环家伟 这次我教大家用代码画一个心,这样你们就可以送给你们的女(男)朋友了.没找到对象的也可以用来表白啊. 1.首先,我去百度找了心形线的函数,如下: 2.  联系高中的数 ...

  4. 手把手带你画一个漂亮蜂窝view Android自定义view

    上一篇做了一个水波纹view  不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...

  5. 手把手带你画一个动态错误提示 Android自定义view

    嗯..再差1篇就可以获得持之以恒徽章了,今天带大家画一个比较简单的view. 转载请注明出处:http://blog.csdn.net/wingichoy/article/details/504771 ...

  6. Android之自定义View以及画一个时钟

    https://www.2cto.com/kf/201509/443112.html 概述: 当Android自带的View满足不了开发者时,自定义View就发挥了很好的作用.建立一个自定义View, ...

  7. 自己画一个ActivityIndicatorView-b

    苹果的UI控件中有一个UIActivityIndicatorView,俗称菊花.→_→现在我们仿照它来制作一个其它样式的指示器,如下: ActivityView.png 自定义指示器 首先画一个白色的 ...

  8. 樱花的季节,教大家用canvas画出飞舞的樱花树

    又到了樱花的季节,教大家使用canvas画出飞舞的樱花树效果. 废话少说,先看效果. 演示效果地址:http://suohb.com/work/tree4.htm 查看演示效果 第一步,我们先画出一棵 ...

  9. 撩妹技能 get,教你用 canvas 画一场流星雨

    开始 妹子都喜欢流星,如果她说不喜欢,那她一定是一个假妹子. 现在就一起来做一场流星雨,用程序员的野路子浪漫一下. 要画一场流星雨,首先,自然我们要会画一颗流星. 玩过 canvas 的同学,你画圆画 ...

随机推荐

  1. Windows下多个JDK版本的切换方法

    问题 因我之前在window中无法命令行输入,后来发现是电脑中存在多个JDK,导致设置混乱. 于是,我继续深入研究了当电脑存在多个JDK的情况下,如何设置想要的JDK版本. 步骤 1.更改环境变量 进 ...

  2. 2019暑假Java学习笔记(三)

    目录 面向对象 对象 构造方法 引用与对象实例 static final 封装 this 继承 super 方法重载与重写 多态 抽象类 接口 内部类 成员内部类 静态内部类 局部内部类 匿名内部类 ...

  3. 解决'python -m pip install --upgrade pip' 报错问题

    再安装包的时候提示 You are using pip version 9.0.3, however version 10.0.1 is available.You should consider u ...

  4. python练习:寒冰猴子狐狸,猫狗咬架

    python练习:寒冰猴子狐狸,猫狗咬架 一,寒冰猴子狐狸 class Person: def __init__(self, na, gen, age, fig): self.name = na se ...

  5. SQLServer ROW_NUMBER()函数使用方法 分区排序

    #ROW_NUMBER() over()能干什么? 既可满足分区的需求,也可以根据一定的顺序来排序. #细细说 select ROW_NUMBER() over(partition by xm Ord ...

  6. docker中mysql pxc集群

    PXC集群 https://hub.docker.com/r/percona/percona-xtradb-cluster 安装PXC镜像 下载镜像或者导入本地镜像 docker pull perco ...

  7. [转] cmake源码编译安装jsoncpp

    1.下载jsoncpp源码 wget https://github.com/open-source-parsers/jsoncpp/archive/master.zip 2.解压缩源码文件 unzip ...

  8. Charles 激活入口以及账号密码

    激活入口 // Charles Proxy License // 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. // Charles 4.2目前是最新版,可用. Registered ...

  9. Pycharm连接远程服务器并进行代码上传+远程调试

    前提:需要有一个远程服务器,知道他的ip.port.user.password 一.连接远程服务器 进入配置页面 Pycharm菜单栏,如下图所示,依次点击 Tools -> Deploymen ...

  10. Qt编写气体安全管理系统27-设备调试

    一.前言 设备调试核心就是将整个系统中的所有打印数据统一显示到一个模块上,一般都会将硬件通信的收发数据和对应的解析信号发出来或者qdebug出来,这个在调试阶段非常有用,可以具体追踪问题出在哪,哪个数 ...