|
@@ -5,10 +5,7 @@ import 'dart:math' as math;
|
|
|
|
|
|
enum CircularStrokeCap { butt, round, square }
|
|
|
|
|
|
-enum ArcType {
|
|
|
- HALF,
|
|
|
- FULL,
|
|
|
-}
|
|
|
+enum ArcType { HALF, FULL, CUSTOM }
|
|
|
|
|
|
// ignore: must_be_immutable
|
|
|
class CircularPercentIndicator extends StatefulWidget {
|
|
@@ -98,8 +95,7 @@ class CircularPercentIndicator extends StatefulWidget {
|
|
|
this.fillColor = Colors.transparent,
|
|
|
this.backgroundColor = const Color(0xFFB8C7CB),
|
|
|
Color progressColor,
|
|
|
- this.backgroundWidth =
|
|
|
- -1, //negative values ignored, replaced with lineWidth
|
|
|
+ this.backgroundWidth = -1, //negative values ignored, replaced with lineWidth
|
|
|
this.linearGradient,
|
|
|
this.animation = false,
|
|
|
this.animationDuration = 500,
|
|
@@ -120,14 +116,13 @@ class CircularPercentIndicator extends StatefulWidget {
|
|
|
this.rotateLinearGradient = false})
|
|
|
: super(key: key) {
|
|
|
if (linearGradient != null && progressColor != null) {
|
|
|
- throw ArgumentError(
|
|
|
- 'Cannot provide both linearGradient and progressColor');
|
|
|
+ throw ArgumentError('Cannot provide both linearGradient and progressColor');
|
|
|
}
|
|
|
_progressColor = progressColor ?? Colors.red;
|
|
|
|
|
|
assert(startAngle >= 0.0);
|
|
|
assert(curve != null);
|
|
|
- if (percent < 0.0 || percent > 1.0) {
|
|
|
+ if (percent < 0.0) {
|
|
|
throw Exception("Percent value must be a double between 0.0 and 1.0");
|
|
|
}
|
|
|
|
|
@@ -137,12 +132,10 @@ class CircularPercentIndicator extends StatefulWidget {
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
- _CircularPercentIndicatorState createState() =>
|
|
|
- _CircularPercentIndicatorState();
|
|
|
+ _CircularPercentIndicatorState createState() => _CircularPercentIndicatorState();
|
|
|
}
|
|
|
|
|
|
-class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
- with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
|
|
+class _CircularPercentIndicatorState extends State<CircularPercentIndicator> with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
|
|
AnimationController _animationController;
|
|
|
Animation _animation;
|
|
|
double _percent = 0.0;
|
|
@@ -158,9 +151,7 @@ class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
@override
|
|
|
void initState() {
|
|
|
if (widget.animation) {
|
|
|
- _animationController = AnimationController(
|
|
|
- vsync: this,
|
|
|
- duration: Duration(milliseconds: widget.animationDuration));
|
|
|
+ _animationController = AnimationController(vsync: this, duration: Duration(milliseconds: widget.animationDuration));
|
|
|
_animation = Tween(begin: 0.0, end: widget.percent).animate(
|
|
|
CurvedAnimation(parent: _animationController, curve: widget.curve),
|
|
|
)..addListener(() {
|
|
@@ -172,8 +163,7 @@ class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
}
|
|
|
});
|
|
|
_animationController.addStatusListener((status) {
|
|
|
- if (widget.onAnimationEnd != null &&
|
|
|
- status == AnimationStatus.completed) {
|
|
|
+ if (widget.onAnimationEnd != null && status == AnimationStatus.completed) {
|
|
|
widget.onAnimationEnd();
|
|
|
}
|
|
|
});
|
|
@@ -185,9 +175,7 @@ class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
}
|
|
|
|
|
|
void _checkIfNeedCancelAnimation(CircularPercentIndicator oldWidget) {
|
|
|
- if (oldWidget.animation &&
|
|
|
- !widget.animation &&
|
|
|
- _animationController != null) {
|
|
|
+ if (oldWidget.animation && !widget.animation && _animationController != null) {
|
|
|
_animationController.stop();
|
|
|
}
|
|
|
}
|
|
@@ -195,15 +183,10 @@ class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
@override
|
|
|
void didUpdateWidget(CircularPercentIndicator oldWidget) {
|
|
|
super.didUpdateWidget(oldWidget);
|
|
|
- if (oldWidget.percent != widget.percent ||
|
|
|
- oldWidget.startAngle != widget.startAngle) {
|
|
|
+ if (oldWidget.percent != widget.percent || oldWidget.startAngle != widget.startAngle) {
|
|
|
if (_animationController != null) {
|
|
|
- _animationController.duration =
|
|
|
- Duration(milliseconds: widget.animationDuration);
|
|
|
- _animation = Tween(
|
|
|
- begin: widget.animateFromLastPercent ? oldWidget.percent : 0.0,
|
|
|
- end: widget.percent)
|
|
|
- .animate(
|
|
|
+ _animationController.duration = Duration(milliseconds: widget.animationDuration);
|
|
|
+ _animation = Tween(begin: widget.animateFromLastPercent ? oldWidget.percent : 0.0, end: widget.percent).animate(
|
|
|
CurvedAnimation(parent: _animationController, curve: widget.curve),
|
|
|
);
|
|
|
_animationController.forward(from: 0.0);
|
|
@@ -235,42 +218,33 @@ class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
|
|
|
children: [
|
|
|
CustomPaint(
|
|
|
painter: CirclePainter(
|
|
|
- progress: _percent * 360,
|
|
|
+ progress: math.min(360, _percent * 360),
|
|
|
progressColor: widget.progressColor,
|
|
|
+ progressOver: math.max(0, _percent -1),
|
|
|
backgroundColor: widget.backgroundColor,
|
|
|
startAngle: widget.startAngle,
|
|
|
circularStrokeCap: widget.circularStrokeCap,
|
|
|
radius: (widget.radius / 2) - widget.lineWidth / 2,
|
|
|
lineWidth: widget.lineWidth,
|
|
|
backgroundWidth: //negative values ignored, replaced with lineWidth
|
|
|
- widget.backgroundWidth >= 0.0
|
|
|
- ? (widget.backgroundWidth)
|
|
|
- : widget.lineWidth,
|
|
|
+ widget.backgroundWidth >= 0.0 ? (widget.backgroundWidth) : widget.lineWidth,
|
|
|
arcBackgroundColor: widget.arcBackgroundColor,
|
|
|
arcType: widget.arcType,
|
|
|
reverse: widget.reverse,
|
|
|
linearGradient: widget.linearGradient,
|
|
|
maskFilter: widget.maskFilter,
|
|
|
rotateLinearGradient: widget.rotateLinearGradient),
|
|
|
- child: (widget.center != null)
|
|
|
- ? Center(child: widget.center)
|
|
|
- : Container(),
|
|
|
+ child: (widget.center != null) ? Center(child: widget.center) : Container(),
|
|
|
),
|
|
|
if (widget.widgetIndicator != null && widget.animation)
|
|
|
Positioned.fill(
|
|
|
child: Transform.rotate(
|
|
|
- angle: radians(
|
|
|
- (widget.circularStrokeCap != CircularStrokeCap.butt &&
|
|
|
- widget.reverse)
|
|
|
- ? -15
|
|
|
- : 0),
|
|
|
+ angle: radians((widget.circularStrokeCap != CircularStrokeCap.butt && widget.reverse) ? -15 : 0),
|
|
|
child: Transform.rotate(
|
|
|
angle: radians((widget.reverse ? -360 : 360) * _percent),
|
|
|
child: Transform.translate(
|
|
|
offset: Offset(
|
|
|
- (widget.circularStrokeCap != CircularStrokeCap.butt)
|
|
|
- ? widget.lineWidth / 2
|
|
|
- : 0,
|
|
|
+ (widget.circularStrokeCap != CircularStrokeCap.butt) ? widget.lineWidth / 2 : 0,
|
|
|
(-widget.radius / 2 + widget.lineWidth / 2),
|
|
|
),
|
|
|
child: widget.widgetIndicator,
|
|
@@ -310,6 +284,7 @@ class CirclePainter extends CustomPainter {
|
|
|
final double lineWidth;
|
|
|
final double backgroundWidth;
|
|
|
final double progress;
|
|
|
+ final double progressOver;
|
|
|
final double radius;
|
|
|
final Color progressColor;
|
|
|
final Color backgroundColor;
|
|
@@ -322,10 +297,13 @@ class CirclePainter extends CustomPainter {
|
|
|
final MaskFilter maskFilter;
|
|
|
final bool rotateLinearGradient;
|
|
|
|
|
|
+ final Paint _paintScale = Paint()..strokeWidth = 1;
|
|
|
+
|
|
|
CirclePainter(
|
|
|
{this.lineWidth,
|
|
|
this.backgroundWidth,
|
|
|
this.progress,
|
|
|
+ this.progressOver,
|
|
|
@required this.radius,
|
|
|
this.progressColor,
|
|
|
this.backgroundColor,
|
|
@@ -380,7 +358,9 @@ class CirclePainter extends CustomPainter {
|
|
|
final rectForArc = Rect.fromCircle(center: center, radius: radius);
|
|
|
double startAngleFixedMargin = 1.0;
|
|
|
if (arcType != null) {
|
|
|
- if (arcType == ArcType.FULL) {
|
|
|
+ if (arcType == ArcType.CUSTOM) {
|
|
|
+ startAngleFixedMargin = 1 - (fixedStartAngle - 180).abs() * 2 / 360;
|
|
|
+ } else if (arcType == ArcType.FULL) {
|
|
|
fixedStartAngle = 220;
|
|
|
startAngleFixedMargin = 172 / fixedStartAngle;
|
|
|
} else {
|
|
@@ -389,8 +369,9 @@ class CirclePainter extends CustomPainter {
|
|
|
}
|
|
|
}
|
|
|
if (arcType == ArcType.HALF) {
|
|
|
- canvas.drawArc(rectForArc, radians(-90.0 + fixedStartAngle),
|
|
|
- radians(360 * startAngleFixedMargin), false, _paintBackground);
|
|
|
+ canvas.drawArc(rectForArc, radians(-90.0 + fixedStartAngle), radians(360 * startAngleFixedMargin), false, _paintBackground);
|
|
|
+ } else if (arcType == ArcType.CUSTOM) {
|
|
|
+ canvas.drawArc(rectForArc, radians(-90.0 + fixedStartAngle), radians(360 * startAngleFixedMargin), false, _paintBackground);
|
|
|
} else {
|
|
|
canvas.drawCircle(center, radius, _paintBackground);
|
|
|
}
|
|
@@ -401,8 +382,7 @@ class CirclePainter extends CustomPainter {
|
|
|
if (linearGradient != null) {
|
|
|
if (rotateLinearGradient && progress > 0) {
|
|
|
double correction = 0;
|
|
|
- if (_paintLine.strokeCap == StrokeCap.round ||
|
|
|
- _paintLine.strokeCap == StrokeCap.square) {
|
|
|
+ if (_paintLine.strokeCap == StrokeCap.round || _paintLine.strokeCap == StrokeCap.square) {
|
|
|
if (reverse) {
|
|
|
correction = math.atan(_paintLine.strokeWidth / 2 / radius);
|
|
|
} else {
|
|
@@ -410,17 +390,12 @@ class CirclePainter extends CustomPainter {
|
|
|
}
|
|
|
}
|
|
|
_paintLine.shader = SweepGradient(
|
|
|
- transform: reverse
|
|
|
- ? GradientRotation(
|
|
|
- radians(-90 - progress + startAngle) - correction)
|
|
|
- : GradientRotation(
|
|
|
- radians(-90.0 + startAngle) - correction),
|
|
|
+ transform:
|
|
|
+ reverse ? GradientRotation(radians(-90 - progress + startAngle) - correction) : GradientRotation(radians(-90.0 + startAngle) - correction),
|
|
|
startAngle: radians(0),
|
|
|
- endAngle: radians(progress),
|
|
|
+ endAngle: radians(360),
|
|
|
tileMode: TileMode.clamp,
|
|
|
- colors: reverse
|
|
|
- ? linearGradient.colors.reversed.toList()
|
|
|
- : linearGradient.colors)
|
|
|
+ colors: reverse ? linearGradient.colors.reversed.toList() : linearGradient.colors)
|
|
|
.createShader(
|
|
|
Rect.fromCircle(
|
|
|
center: center,
|
|
@@ -437,19 +412,6 @@ class CirclePainter extends CustomPainter {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fixedStartAngle = startAngle;
|
|
|
-
|
|
|
- startAngleFixedMargin = 1.0;
|
|
|
- if (arcType != null) {
|
|
|
- if (arcType == ArcType.FULL) {
|
|
|
- fixedStartAngle = 220;
|
|
|
- startAngleFixedMargin = 172 / fixedStartAngle;
|
|
|
- } else {
|
|
|
- fixedStartAngle = 270;
|
|
|
- startAngleFixedMargin = 135 / fixedStartAngle;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
if (arcBackgroundColor != null) {
|
|
|
canvas.drawArc(
|
|
|
Rect.fromCircle(center: center, radius: radius),
|
|
@@ -460,9 +422,32 @@ class CirclePainter extends CustomPainter {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ if (arcType == ArcType.CUSTOM) {
|
|
|
+ if(progressOver > 0) {
|
|
|
+ canvas.save();
|
|
|
+ var rect = RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(center.dy, lineWidth + 5), width: 2, height: 3), Radius.circular(5.0));
|
|
|
+ canvas.translate(center.dx, center.dy);
|
|
|
+ canvas.rotate(radians(fixedStartAngle));
|
|
|
+ canvas.translate(-center.dx, -center.dy);
|
|
|
+ var count = 60;
|
|
|
+ var r = radians(360 * startAngleFixedMargin) / count;
|
|
|
+ for (var i = 0; i <= count; i++) {
|
|
|
+ if (progressOver >= i / count) {
|
|
|
+ _paintScale.color = _setColor(i, count);
|
|
|
+ } else {
|
|
|
+ _paintScale.color = backgroundColor;
|
|
|
+ }
|
|
|
+ canvas.drawRRect(rect, _paintScale);
|
|
|
+ canvas.translate(center.dx, center.dy);
|
|
|
+ canvas.rotate(r);
|
|
|
+ canvas.translate(-center.dx, -center.dy);
|
|
|
+ }
|
|
|
+ canvas.restore();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (reverse) {
|
|
|
- final start =
|
|
|
- radians(360 * startAngleFixedMargin - 90.0 + fixedStartAngle);
|
|
|
+ final start = radians(360 * startAngleFixedMargin - 90.0 + fixedStartAngle);
|
|
|
final end = radians(-progress * startAngleFixedMargin);
|
|
|
canvas.drawArc(
|
|
|
Rect.fromCircle(
|
|
@@ -490,6 +475,17 @@ class CirclePainter extends CustomPainter {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ final Color _o = const Color(0xff16A2FF);
|
|
|
+ final Color _o1 = const Color(0xff8DF7FF);
|
|
|
+ _setColor(int val,int mCount) {
|
|
|
+ double one = (255 + 255) / (mCount * 2 / 3);
|
|
|
+ int r = 0, g = 0, b = 0;
|
|
|
+ r = (_o.red + (_o1.red - _o.red) * val / mCount).toInt();
|
|
|
+ g = (_o.green + (_o1.green - _o.green) * val / mCount).toInt();
|
|
|
+ b = (_o.blue + (_o1.blue - _o.blue) * val / mCount).toInt();
|
|
|
+ int d = -val;
|
|
|
+ return Color.fromRGBO(r,g,b, 1.0);
|
|
|
+ }
|
|
|
@override
|
|
|
bool shouldRepaint(CustomPainter oldDelegate) {
|
|
|
return true;
|