progress_bar.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import 'dart:math' as math;
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/painting.dart';
  4. class CircularProgressBar extends StatefulWidget {
  5. final double percent;
  6. final double radius;
  7. final double width;
  8. final Widget? center;
  9. CircularProgressBar(
  10. {Key? key,
  11. this.percent = 0.0,
  12. this.radius = 50.0,
  13. this.width = 10.0,
  14. this.center});
  15. @override
  16. State<StatefulWidget> createState() => _State();
  17. }
  18. class _State extends State<CircularProgressBar>
  19. with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
  20. late AnimationController _animationController;
  21. late Animation _animation;
  22. double _percent = 0.0;
  23. @override
  24. void dispose() {
  25. _animationController.dispose();
  26. super.dispose();
  27. }
  28. @override
  29. void initState() {
  30. _animationController = AnimationController(
  31. vsync: this, duration: Duration(milliseconds: 1000));
  32. _animation = Tween(begin: 0.0, end: widget.percent).animate(
  33. CurvedAnimation(parent: _animationController, curve: Curves.linear),
  34. )..addListener(() {
  35. setState(() {
  36. _percent = _animation.value;
  37. });
  38. });
  39. _animationController.addStatusListener((status) {});
  40. _animationController.forward();
  41. super.initState();
  42. }
  43. @override
  44. void didUpdateWidget(CircularProgressBar oldWidget) {
  45. super.didUpdateWidget(oldWidget);
  46. if (oldWidget.percent != widget.percent) {
  47. if (_animationController != null) {
  48. _animationController.duration = Duration(milliseconds: 1000);
  49. _animation = Tween(begin: 0.0, end: widget.percent).animate(
  50. CurvedAnimation(parent: _animationController, curve: Curves.linear),
  51. );
  52. _animationController.forward(from: 0.0);
  53. } else {
  54. _updateProgress();
  55. }
  56. }
  57. }
  58. _updateProgress() {
  59. setState(() {
  60. _percent = widget.percent;
  61. });
  62. }
  63. @override
  64. bool get wantKeepAlive => true;
  65. @override
  66. Widget build(BuildContext context) {
  67. super.build(context);
  68. return Container(
  69. height: widget.radius * 2 + widget.width,
  70. width: widget.radius * 2,
  71. child: AnimatedBuilder(
  72. animation: _animation,
  73. builder: (BuildContext context, Widget? child) {
  74. return Padding(
  75. padding: const EdgeInsets.symmetric(vertical: 10.0),
  76. child: CustomPaint(
  77. painter: ProgressBarThree(_animation.value,
  78. width: widget.width,
  79. radius: widget.radius - widget.width / 2),
  80. child: (widget.center != null)
  81. ? Center(child: widget.center)
  82. : Container(),
  83. ),
  84. );
  85. },
  86. ));
  87. }
  88. }
  89. class ProgressBarThree extends CustomPainter {
  90. final double progress;
  91. final double width;
  92. final double radius;
  93. final Paint _bg = Paint()
  94. ..isAntiAlias = true
  95. ..color = const Color(0xfff1f1f1)
  96. ..style = PaintingStyle.stroke;
  97. final List<Paint> _p = [
  98. Paint()
  99. ..isAntiAlias = true
  100. ..color = const Color(0xffFFE600)
  101. ..style = PaintingStyle.stroke,
  102. Paint()
  103. ..isAntiAlias = true
  104. ..color = const Color(0xffFFAA00)
  105. ..style = PaintingStyle.stroke,
  106. Paint()
  107. ..isAntiAlias = true
  108. ..color = const Color(0xffFF7323)
  109. ..style = PaintingStyle.stroke
  110. ];
  111. ProgressBarThree(this.progress, {this.width = 10.0, this.radius = 50.0});
  112. @override
  113. void paint(Canvas canvas, Size size) {
  114. final center = Offset(size.width / 2, size.height / 2);
  115. final rect = Rect.fromCircle(center: center, radius: this.radius);
  116. double max = math.pi * 2;
  117. double start = math.pi * 3 / 2;
  118. _bg.strokeWidth = width;
  119. int split = 3;
  120. double _angleEnd = max / split;
  121. double _endDiff = (_angleEnd - _angleEnd * 0.98) / 2;
  122. start += _endDiff;
  123. double _angle = start;
  124. for (var i = 0; i < split; i++) {
  125. canvas.drawArc(rect, _angle, _angleEnd * 0.98, false, _bg);
  126. _angle += _angleEnd;
  127. }
  128. double _progress = 1.0 / split;
  129. start = math.pi * 3 / 2;
  130. start += _endDiff;
  131. _angle = start;
  132. double _progressTotal = math.min(this.progress, 1.0);
  133. for (var i = 0; i < split; i++) {
  134. double _percent = _progressTotal / _progress;
  135. _p[i].strokeWidth = width;
  136. canvas.drawArc(rect, _angle, _angleEnd * 0.98 * math.min(_percent, 1.0),
  137. false, _p[i]);
  138. _angle += _angleEnd;
  139. _progressTotal -= _progress;
  140. if (_progressTotal < 0) break;
  141. }
  142. }
  143. @override
  144. bool shouldRepaint(CustomPainter oldDelegate) => this != oldDelegate;
  145. }