import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; const halfPi = pi / 2; const pi = math.pi; typedef LabelBuilder = Widget Function(double value); class RingIndicator extends StatefulWidget { const RingIndicator({ Key? key, this.backgroundColor = Colors.grey, this.ringWidth = 10, this.startAngel = 0, this.endAngel = pi * 2, required this.ringColor, required this.maxValue, this.minValue = 0, required this.value, this.innerRing, this.labelBuilder, }) : super(key: key); final Color backgroundColor; final double ringWidth; final Color ringColor; final double startAngel; final double endAngel; final double maxValue; final double minValue; final double value; final RingIndicator? innerRing; final LabelBuilder? labelBuilder; @override State createState() => _RingIndicatorState(); } class _RingIndicatorState extends State { double? _value = 0; @override Widget build(BuildContext context) { var value = _value ?? widget.value; var fillAngel = ((value - widget.minValue) / (widget.maxValue - widget.minValue)) * (widget.endAngel - widget.startAngel); return Center( child: SizedBox.expand( child: CustomPaint( painter: _RingIndicatorPainter( backgroundColor: widget.backgroundColor, ringWidth: widget.ringWidth, startAngel: widget.startAngel, endAngel: widget.endAngel, ringColor: widget.ringColor, fillAngel: fillAngel, ), child: Stack( children: [ if (widget.labelBuilder != null) widget.labelBuilder!(value), ], ), ), ), ); } Ticker? ticker; @override void didUpdateWidget(RingIndicator oldWidget) { super.didUpdateWidget(oldWidget); var startTime = DateTime.now(); ticker?.stop(); ticker = Ticker((_) { var diff = DateTime.now().difference(startTime).inMilliseconds; if (diff > 300) { return ticker?.stop(); } if (mounted) { setState(() { _value = oldWidget.value + (widget.value - oldWidget.value) * (diff / 300); }); } }) ..start(); } } class _RingIndicatorPainter extends CustomPainter { _RingIndicatorPainter({ required this.backgroundColor, required this.ringWidth, required this.ringColor, required this.startAngel, required this.fillAngel, required this.endAngel, }) : _paint = Paint() ..strokeWidth = ringWidth ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; final Color backgroundColor; final double ringWidth; final Color ringColor; final double startAngel; final double fillAngel; final double endAngel; final Paint _paint; @override void paint(Canvas canvas, Size size) { canvas.drawArc( Rect.fromCenter( center: size.center(Offset.zero), width: size.width, height: size.height, ), startAngel - halfPi, endAngel - startAngel, false, _paint..color = backgroundColor, ); canvas.drawArc( Rect.fromCenter( center: size.center(Offset.zero), width: size.width, height: size.height, ), startAngel - halfPi, fillAngel, false, _paint..color = ringColor, ); } @override bool shouldRepaint(covariant _RingIndicatorPainter oldDelegate) => true; }