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 的同学,你画圆画 ...
随机推荐
- c++与matcom混合编程
#include #include #include "matlib.h" using namespace std; int main() { initM(MATCOM_VERSI ...
- css3学习之--伪类与圆角
随着css3.0的发布到逐渐完善,目前已经大部分浏览器已经能较好地适配,所以写一些css3的学习经历,分享心得,主要以案例讲解为主,话不多说,今天以css3的新增的“圆角”属性来讲解,基于web画一个 ...
- git,指南,操作
助你开始使用 git 的简易指南,木有高深内容,;). Tweet 作者:罗杰·杜德勒 感谢:@tfnico, @fhd and Namics其他语言 english, deutsch, españo ...
- Maven在jar中生成重复的pom.xml和pom.properties文件
eclispe maven打包的时候总是出现"生成的jar的META-INF中,重复的pom.xml和pom.properties文件.",maven命令直接打包则没有这个问题. ...
- Java架构师之路(参考这个学习吧)
- 使用yarn代替npm作为node.js的模块管理器
使用yarn代替npm作为node.js的模块管理器 转 https://www.jianshu.com/p/bfe96f89da0e Fast, reliable, and secure d ...
- Uni-app 使用总结
1.去掉顶部导航 在page.json中添加 "globalStyle" : { "navigationBarTextStyle" : "black& ...
- PAT 甲级 1086 Tree Traversals Again (25分)(先序中序链表建树,求后序)***重点复习
1086 Tree Traversals Again (25分) An inorder binary tree traversal can be implemented in a non-recu ...
- (转)JVM原理讲解和调优
背景:jvm实际调优在面试时候经常被问到,所以有必要认真总结一番. 转自:JVM原理讲解和调优 四.JVM内存调优 首先需要注意的是在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存 ...
- poj 2775 文件结构“图"
总时间限制: 1000ms 内存限制: 65536kB 描述 在计算机上看到文件系统的结构通常很有用.Microsoft Windows上面的"explorer"程序就是这样的一个 ...