Flutter:教你用CustomPaint画一个自定义的CircleProgressBar
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的更多相关文章
- 使用CAShapeLayer和UIBezierPath画一个自定义半圆弧button
通常我们使用系统自带的UIButton时,一般都是Rect矩形形式的,或则美工给出一张半圆弧的按钮,如图为一张半圆加三角形的按钮,而此时,如果给按钮添加点击事件时,响应事件依然为矩形区域,不符合我们的 ...
- 手对手的教你用canvas画一个简单的海报
啦啦啦,首先说下需求,产品想让用户在我们app内,分享一张图片到微信.qq等平台.图片中包含用户的姓名.头像.和带着自己信息的二维码.然后,如何生成这张海报呢~~~首先我们老大告诉我有一个插件叫htm ...
- 用C语言画一个心
用C语言图形库画一个心 --环家伟 这次我教大家用代码画一个心,这样你们就可以送给你们的女(男)朋友了.没找到对象的也可以用来表白啊. 1.首先,我去百度找了心形线的函数,如下: 2. 联系高中的数 ...
- 手把手带你画一个漂亮蜂窝view Android自定义view
上一篇做了一个水波纹view 不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...
- 手把手带你画一个动态错误提示 Android自定义view
嗯..再差1篇就可以获得持之以恒徽章了,今天带大家画一个比较简单的view. 转载请注明出处:http://blog.csdn.net/wingichoy/article/details/504771 ...
- Android之自定义View以及画一个时钟
https://www.2cto.com/kf/201509/443112.html 概述: 当Android自带的View满足不了开发者时,自定义View就发挥了很好的作用.建立一个自定义View, ...
- 自己画一个ActivityIndicatorView-b
苹果的UI控件中有一个UIActivityIndicatorView,俗称菊花.→_→现在我们仿照它来制作一个其它样式的指示器,如下: ActivityView.png 自定义指示器 首先画一个白色的 ...
- 樱花的季节,教大家用canvas画出飞舞的樱花树
又到了樱花的季节,教大家使用canvas画出飞舞的樱花树效果. 废话少说,先看效果. 演示效果地址:http://suohb.com/work/tree4.htm 查看演示效果 第一步,我们先画出一棵 ...
- 撩妹技能 get,教你用 canvas 画一场流星雨
开始 妹子都喜欢流星,如果她说不喜欢,那她一定是一个假妹子. 现在就一起来做一场流星雨,用程序员的野路子浪漫一下. 要画一场流星雨,首先,自然我们要会画一颗流星. 玩过 canvas 的同学,你画圆画 ...
随机推荐
- modao账户
chairman987@163.com 墨刀注册 p@ssw0rd OR 123456
- 剑指offer:数组中只出现一次的数字
题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路分析: 1. 直接想法,每个数字遍历,统计出现次数,复杂度O(n^2),超时. 2. 借助 ...
- Cannot capture jmeter traffic in fiddler
Cannot capture jmeter traffic in fiddler First, change Fiddler's port back to 8888 as it was origina ...
- Java_jdbc 基础笔记之十 数据库连接 (ResultSetMetaData 类)
ResultSetMetaData 类 调用ResultSet 的getMetaData()方法得到ResultSetMetaData 类对象: 可用于获取关于 ResultSet 对象中列的类型和属 ...
- InvalidSelectorError: Compound class names not permitted报错处理
InvalidSelectorError: Compound class names not permitted报错处理 环境:python3.6 + selenium 3.11 + chromed ...
- pip常用命令(转载)
用阿里云服务器,使用pip安装第三方库的时候卡的要死.所以我就想pip能不能安装本地的包. 找到了这篇博客: http://me.iblogc.com/2015/01/01/pip%E5%B8%B8% ...
- hive中同源多重insert写法
多重insert: with tmp_a as ( select name from tmp_test3 ) from tmp_a insert overwrite table tmp_test1 s ...
- Java12新特性 -- Shenandoah GC
Shenandoah 垃圾回收器是 Red Hat 在 2014 年宣布进行的一项垃圾收集器研究项目 Pauseless GC 的实现,旨在针对 JVM 上的内存收回实现低停顿的需求.该设计将与应用程 ...
- 几种常见的java网页静态化技术对比
名称 优点 缺点 使用场景 jsp 1.功能强大,可以写java代码 2.支持jsp标签(jsp tag) 3.支持表达式语言(el) 4.官方标准,用户群广,丰富的第三方jsp标签库 5.性能良好. ...
- matlab学习笔记10_5 通用字符串操作和比较函数
一起来学matlab-matlab学习笔记10 10_5 通用字符串操作和比较函数 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张 ...