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. MongoDB 数据库备份还原

    数据库备份 在 Mongodb 中我们使用 mongodump 命令来备份 MongoDB 数据.该命令可以导出所有数据 到指定目录中. mongodump 命令可以通过参数指定导出的数据量级转存的服 ...

  2. Lomok @Data使用

    看了廖师兄的Springboot视频发现很多很好玩的小工具,lombok就是其中一个.lombok是一个可以通过简单的注解的形式来帮助我们简化消除一些必须有但显得很臃肿的 Java 代码的工具,简单来 ...

  3. 2019 SDN第五次上机作业

    2019 SDN第五次上机作业 作业链接 1.浏览RYU官网学习RYU控制器的安装和RYU开发入门教程,提交对于教程代 码的理解,包括但不限于: 安装RYU控制器并测试 安装教程 安装过程及遇到各种问 ...

  4. Mybatis(上)

    Mybatis 一.MyBatis 简介 1. MyBatis作用 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架. MyBatis 避免了几乎所有的 JDBC 代码和手 ...

  5. 第06组 Alpha冲刺(1/4)

    队名:福大帮 组长博客链接:https://www.cnblogs.com/mhq-mhq/p/11863075.html 作业博客 :https://edu.cnblogs.com/campus/f ...

  6. em,rem,px的区别,以及实现原理?

    px像素(Pixel).相对长度单位.像素px是相对于显示器屏幕分辨率而言的.em是相对长度单位.相对于当前对象内文本的字体尺寸举个例子:比如说当前容器`font-size:16px;`则`1em`就 ...

  7. 最近b站好像把blv格式换成m4s,改成mp4之后没有声音,

    我研究了几个小时,然后知道一个方法,但是必须有电脑.1.m4s 的视频改为mp4可以拖进pr2.m4s的音频不能直接拖进pr(会报错),改为mp3也一样,要先改为mp3,然后在格式工厂里面选择,mp3 ...

  8. Python实例100个(基于最新Python3.7版本)

    Python3 100例 原题地址:   http://www.runoob.com/python/python-100-examples.html    git地址:    https://gith ...

  9. Docker打包部署前端项目与负载均衡

    设置淘宝镜像 npm install -g cnpm --registry=https://registry.npm.taobao.org //在home/fn1 home/fn2放入项目和nginx ...

  10. kafka与Rocketmq的区别

    淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介,可完全水平扩容,为了进一步降低成本,我们认为存储部分可以进一步优化,2011年初,Linkin开源了Kaf ...