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. CentOS 7 安装 MySql8.0

    1-配置 Yum 库 rpm -Uvh https://repo.mysql.com/mysql80-community-release-el7-3.noarch.rpm 2-安装 MySql 2.1 ...

  2. 剑指offer:整数中1出现的次数

    题目描述: 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了 ...

  3. python opencv PyQt5

    import cv2 import numpy as np import sys from PyQt5.QtGui import * from PyQt5.QtCore import * from P ...

  4. service和Dao层有什么关系

    Dao层:主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类 ...

  5. pycharm安装pyinstaller将pygame打包成exe

    首先,使用pycharm自带的下载包工具,File-Settings-Project Interpreter,如图: 安装完成后,发现安装到了Python根目录下,我的在C:\python34\Scr ...

  6. Flutter中管理路由栈的方法和应用

    原文地址:https://www.jianshu.com/p/5df089d360e4 本文首先讲的Flutter中的路由,然后主要讲下Flutter中栈管理的几种方法. 了解下Route和Navig ...

  7. 【JS】AJAX跨域-JSONP解决方案(一)

    AJAX跨域介绍 AJAX 跨域访问是用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面 由于安全方面的原因, 客户端js使用xmlhttprequest只能用来向来源网站发送请求 ...

  8. Qt编写安防视频监控系统13-视频存储

    一.前言 一般视频监控行业都会选择把视频存储在本地NVR或者服务器上,而不是存储在客户端电脑,只有当用户经费预算有限的时候,或者用户特殊需求要求存储在本地客户端电脑的时候才会开启存储到本地,正常来说视 ...

  9. [Algorithm] Graph

    图的概念有点多,在此只讨论最基础的内容,所以选择比较薄的高教版<数据结构>. 1.4 非线性数据结构--图 ........................................ ...

  10. copyProperties 忽略null值字段

    在做项目时遇到需要copy两个对象之间的属性值,但是有源对象有null值,在使用BeanUtils来copy时null值会覆盖目标对象的同名字段属性值,然后采用以下方法找到null值字段,然后忽略: ...