diff --git a/example/lib/main.dart b/example/lib/main.dart index d6388bc..9004ce8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -134,20 +134,48 @@ class _MyHomePageState extends State }); }, ), - Stack( - alignment: Alignment.center, - children: [ - Container( - width: 150, - height: 150, - padding: EdgeInsets.all(5), - child: CircularProgressIndicator( - value: _segmentValue / 10, - strokeWidth: 10.0, - valueColor: _colorTween, - ), + Row( + children: [ + Spacer(), + Stack( + alignment: Alignment.center, + children: [ + Container( + width: 150, + height: 150, + padding: EdgeInsets.all(5), + child: CircularProgressIndicator( + value: _segmentValue / 10, + strokeWidth: 10.0, + valueColor: _colorTween, + ), + ), + Text("${_segmentValue / 10 * 100}%"), + ], ), - Text("${_segmentValue / 10 * 100}%"), + Spacer(), + Stack( + alignment: Alignment.center, + children: [ + Container( + width: 150, + height: 150, + padding: EdgeInsets.all(5), + child: AirDashboardStateProgressIndicator( + size: Size(150, 150), + value: _segmentValue / 10 * 100, //1~100 + valueColor: + ColorTween(begin: Colors.grey, end: Colors.blue) + .transform(_segmentValue / 10), + pathStrokeWidth: 10, + valueStrokeWidth: 10, + gapDegree: 60, + ), + ), + Text("${_segmentValue / 10 * 100}%"), + ], + ), + Spacer(), ], ), //圆环、扇形样式的进度 diff --git a/example/pubspec.lock b/example/pubspec.lock index 4c29493..5c17343 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,7 +7,7 @@ packages: path: ".." relative: true source: path - version: "0.1.0" + version: "0.1.1" archive: dependency: transitive description: diff --git a/lib/ai_progress.dart b/lib/ai_progress.dart index 0e6a6ba..cdcbbbf 100644 --- a/lib/ai_progress.dart +++ b/lib/ai_progress.dart @@ -2,5 +2,6 @@ library ai_progress; /// export export 'src/circular_state_progress_indicator.dart'; +export 'src/dashboard_state_progress_indicator.dart'; export 'src/linear_state_progress_indicator.dart'; export 'src/step_state_progress_indicator.dart'; diff --git a/lib/src/circular_state_progress_indicator.dart b/lib/src/circular_state_progress_indicator.dart index 31abab6..2fab24b 100644 --- a/lib/src/circular_state_progress_indicator.dart +++ b/lib/src/circular_state_progress_indicator.dart @@ -5,6 +5,15 @@ import 'progress_mixin.dart'; /// CircularStateProgressIndicator // ignore: must_be_immutable class AirCircularStateProgressIndicator extends StatefulWidget { + /// Limited min value + static const double LIMITED_MIN_VALUE = 0; + + /// Limited max value + static const double LIMITED_MAX_VALUE = 100; + + /// default value + static const double DEFAULT_VALUE = 10; + Size _size; double _min; double _max; @@ -18,19 +27,30 @@ class AirCircularStateProgressIndicator extends StatefulWidget { /// constructor AirCircularStateProgressIndicator({ - Size size, - double min, - double max, - num value, - Color pathColor, + @required Size size, + double min = LIMITED_MIN_VALUE, + double max = LIMITED_MAX_VALUE, + num value = DEFAULT_VALUE, + Color pathColor = Colors.white, Color valueColor = Colors.green, double pathStrokeWidth = 5, double valueStrokeWidth = 5, bool filled = false, bool useCenter = false, }) { + assert(size != null); + assert(min >= LIMITED_MIN_VALUE); + assert(max <= LIMITED_MAX_VALUE); + assert(value >= min); + assert(value <= max); + assert(pathColor != null); + assert(valueColor != null); + assert(pathStrokeWidth != null); + assert(valueStrokeWidth != null); + assert(filled != null); + assert(useCenter != null); _size = size; - //value >= 0.00,value<=100 + //value _value = value; _min = min; _max = max; @@ -98,8 +118,16 @@ class _CircularStateProgressIndicatorState /// CircularProgressPaint class CircularProgressPaint extends CustomPainter with ProgressMixin { - final num maxSweepAngle = 6.5; + /// The max sweep angle exclude stroke cap + final num maxSweepAngleExcludeCap = 6.2; + + /// The max sweep angle include stroke cap + final num maxSweepAngleIncludeCap = 6.5; final num minSweepAngle = 1.0; + + /// start angle + static const double DEFAULT_START_ANGLE = -1.5; + bool _shouldRepaint; num _min; num _max; @@ -157,32 +185,36 @@ class CircularProgressPaint extends CustomPainter with ProgressMixin { } double _getStartAngle() { - return -1.5; + return DEFAULT_START_ANGLE; } double _getSweepAngle() { - if (_value == 0) { + if (_value == _min) { return 0; } - if (_value > 0 && _value < 50) { + if (_value > _min && _value < maxHalf) { return (_value * 0.0612); } - if (_value == 50) { + if (_value == maxHalf) { return _value * 0.0600; } - if (_value > 50 && _value < 100) {} - if (_value == 100) { - return 6.5; + if (_value > maxHalf && _value < _max) {} + if (_value == _max) { + return maxSweepAngleIncludeCap; } return (_value * 0.0615); } + /// the half of max value + get maxHalf => _max / 2; + /// useCenter /// whether use center point close the path. bool get useCenter { return _useCenter; } + /// circular radius double _getRadius({Size size}) { double radius = size.width / 2 < size.height / 2 ? size.width / 2 : size.height / 2; @@ -190,6 +222,7 @@ class CircularProgressPaint extends CustomPainter with ProgressMixin { return radius; } + /// point Offset _getOffset({Size size}) { return Offset(size.width / 2, size.height / 2); } diff --git a/lib/src/dashboard_state_progress_indicator.dart b/lib/src/dashboard_state_progress_indicator.dart new file mode 100644 index 0000000..e52f19b --- /dev/null +++ b/lib/src/dashboard_state_progress_indicator.dart @@ -0,0 +1,246 @@ +import 'package:flutter/material.dart'; + +import 'progress_mixin.dart'; + +/// +const double LIMITED_MIN_VALUE = 0; +const double LIMITED_MAX_VALUE = 100; +const double DEFAULT_VALUE = 10; + +/// Gap degree limited min and max value +const int LIMITED_MIN_GAP_DEGREE = 0; +const int LIMITED_MAX_GAP_DEGREE = 360; +const int DEFAULT_GAP_DEGREE = 60; +const int UNIT_GAP_DEGREE = 60; + +const double DEFAULT_STROKE_WIDTH = 5; + +/// DashboardStateProgressIndicator +// ignore: must_be_immutable +class AirDashboardStateProgressIndicator extends StatefulWidget { + Size _size; + double _min; + double _max; + num _value; + Color _pathColor; + Color _valueColor; + double _pathStrokeWidth; + double _valueStrokeWidth; + bool _filled; + bool _useCenter; + bool _roundCap; + + double _gapDegree; + + /// constructor + AirDashboardStateProgressIndicator({ + Size size, + double min = LIMITED_MIN_VALUE, + double max = LIMITED_MAX_VALUE, + num value = DEFAULT_VALUE, + int gapDegree = DEFAULT_GAP_DEGREE, + Color pathColor = Colors.white, + Color valueColor = Colors.green, + double pathStrokeWidth = DEFAULT_STROKE_WIDTH, + double valueStrokeWidth = DEFAULT_STROKE_WIDTH, + bool filled = false, + bool useCenter = false, + bool roundCap = true, + }) { + assert(size != null); + assert(min >= LIMITED_MIN_VALUE); + assert(max <= LIMITED_MAX_VALUE); + assert(value >= min); + assert(value <= max); + assert(gapDegree >= LIMITED_MIN_GAP_DEGREE); + assert(gapDegree <= LIMITED_MAX_GAP_DEGREE); + assert(pathColor != null); + assert(valueColor != null); + assert(pathStrokeWidth != null); + assert(valueStrokeWidth != null); + assert(filled != null); + assert(useCenter != null); + assert(roundCap != null); + _size = size; + //value >= 0.00,value<=100 + _value = value; + _gapDegree = gapDegree / UNIT_GAP_DEGREE; + _min = min; + _max = max; + //color + _pathColor = pathColor; + _valueColor = valueColor; + //stroke width + _pathStrokeWidth = pathStrokeWidth; + _valueStrokeWidth = valueStrokeWidth; + + //paint round cap + _roundCap = roundCap; + // + _filled = filled; + _useCenter = useCenter; + + //The strokeWidth is zero when _filled is true, + if (_filled) { + _pathStrokeWidth = 0.0; + _valueStrokeWidth = 0.0; + } + } + + @override + State createState() { + return _DashboardStateProgressIndicatorState(); + } +} + +/// _DashboardStateProgressIndicatorState +class _DashboardStateProgressIndicatorState + extends State { + @override + Widget build(BuildContext context) { + return CustomPaint( + size: widget._size, + painter: DashboardProgressPaint._( + shouldRepaint: false, + division: LIMITED_MAX_GAP_DEGREE ~/ UNIT_GAP_DEGREE, + min: widget._min, + max: widget._max, + value: widget._value, + gapDegree: widget._gapDegree, + pathPaint: _getPathPaint(), + valuePaint: _getValuePaint(), + useCenter: widget._useCenter, + ), + ); + } + + Paint _getPathPaint() { + return Paint() + ..color = widget._pathColor + ..strokeWidth = widget._pathStrokeWidth + ..strokeCap = widget._roundCap ? StrokeCap.round : StrokeCap.square + ..strokeJoin = widget._roundCap ? StrokeJoin.round : StrokeJoin.bevel + ..style = widget._filled ? PaintingStyle.fill : PaintingStyle.stroke; + } + + Paint _getValuePaint() { + return Paint() + ..color = widget._valueColor + ..strokeWidth = widget._valueStrokeWidth + ..strokeCap = widget._roundCap ? StrokeCap.round : StrokeCap.square + ..strokeJoin = widget._roundCap ? StrokeJoin.round : StrokeJoin.bevel + ..style = widget._filled ? PaintingStyle.fill : PaintingStyle.stroke; + } +} + +/// DashboardProgressPaint +class DashboardProgressPaint extends CustomPainter with ProgressMixin { + static const int GAP_DEGREE_DIVISION = 6; + static const PI = 3.1415926; + final num maxSweepAngle = 6.5; + final num circleMaxSweepAngle = 6.1275; + final num minSweepAngle = 1.0; + bool _shouldRepaint; + num _min; + num _max; + num _value; + Paint _pathPaint; + Paint _valuePaint; + bool _useCenter; + double _gapDegree; + int _division; + + /// CircularProgressPaint + /// + DashboardProgressPaint._({ + @required bool shouldRepaint, + @required int division, + @required num min, + @required num max, + @required num value, + @required double gapDegree, + @required Paint pathPaint, + @required Paint valuePaint, + @required bool useCenter, + }) { + _shouldRepaint = shouldRepaint; + _division = division; + _min = min; + _max = max; + _value = value; + _gapDegree = gapDegree; + _pathPaint = pathPaint; + _valuePaint = valuePaint; + _useCenter = useCenter; + } + @override + void paint(Canvas canvas, Size size) { + // draw progress path + drawProgressPath(canvas: canvas, paint: _pathPaint, size: size); + + // draw progress value + drawProgressValue(canvas: canvas, paint: _valuePaint, size: size); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return _shouldRepaint; + } + + @override + drawProgressPath({Canvas canvas, Paint paint, Size size}) { + Offset center = _getOffset(size: size); + double radius = _getRadius(size: size); + + canvas.drawArc(Rect.fromCircle(center: center, radius: radius), + _getStartAngle(), _getMaxSweepAngle(), useCenter, paint); + } + + @override + drawProgressValue({Canvas canvas, Paint paint, Size size}) { + Offset center = _getOffset(size: size); + double radius = _getRadius(size: size); + canvas.drawArc(Rect.fromCircle(center: center, radius: radius), + _getStartAngle(), _getValueSweepAngle(), useCenter, paint); + } + + double get gapDegree => _gapDegree; + int get division => _division; + + double _getCircleMaxAngle() { + return circleMaxSweepAngle; + } + + double _getStartAngle() { + return _getGapDegreeSweepAngle() / 2 + maxSweepAngle / 4; + } + + double _getGapDegreeSweepAngle() { + return (maxSweepAngle / division) * _gapDegree; + } + + double _getMaxSweepAngle() { + return _getCircleMaxAngle() - _getGapDegreeSweepAngle(); + } + + double _getValueSweepAngle() { + return _getMaxSweepAngle() / _max * _value; + } + + /// useCenter + /// whether use center point close the path. + bool get useCenter { + return _useCenter; + } + + double _getRadius({Size size}) { + double radius = + size.width / 2 < size.height / 2 ? size.width / 2 : size.height / 2; + radius -= _valuePaint.strokeWidth / 2; + return radius; + } + + Offset _getOffset({Size size}) { + return Offset(size.width / 2, size.height / 2); + } +}