import 'dart:ui'; import 'package:flutter/Material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class PieData { final String label; final double value; final Color color; PieData(this.label, this.value, this.color); } class MultiSegmentCircleIndicator extends StatelessWidget { final List segments; final double radius; final double strokeWidth; final Duration animationDuration; final String Function(double percent)? centerTextBuilder; final String? subtitle; final double centerValue; const MultiSegmentCircleIndicator({ super.key, required this.segments, this.radius = 60, this.strokeWidth = 12, this.animationDuration = const Duration(seconds: 1), this.centerTextBuilder, required this.centerValue, this.subtitle, }); @override Widget build(BuildContext context) { return TweenAnimationBuilder( tween: Tween(begin: 0, end: 1), duration: animationDuration, builder: (context, value, child) { final animatedSegments = segments .map((e) => PieData(e.label, e.value * value, e.color)) .toList(); return Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: radius * 2, height: radius * 2, child: CustomPaint( painter: _CirclePainter( segments: animatedSegments, strokeWidth: strokeWidth, ), child: Center( child: Column( mainAxisSize: MainAxisSize.min, textBaseline: TextBaseline.alphabetic, children: [ Text.rich(TextSpan(children: [ TextSpan( text: centerTextBuilder?.call(centerValue * value) ?? (centerValue * value).toStringAsFixed(0), style: TextStyle( color: Colors.white.withAlpha(229), fontSize: 30.sp, fontWeight: FontWeight.w400, ), ), TextSpan( text: '%', style: TextStyle( color: Colors.white.withAlpha(229), fontSize: 13.03.sp, fontWeight: FontWeight.w500, ), ), ])), if (subtitle != null) Text( subtitle!, style: TextStyle( fontSize: 14, color: Colors.white.withOpacity(0.6), ), ), ], ), ), ), ), const SizedBox(height: 12), ], ); }, ); } } class _CirclePainter extends CustomPainter { final List segments; final double strokeWidth; _CirclePainter({required this.segments, this.strokeWidth = 16}); @override void paint(Canvas canvas, Size size) { final center = size.center(Offset.zero); final radius = (size.shortestSide - strokeWidth) / 2; final paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round; double startAngle = -90.0; for (final segment in segments) { final sweepAngle = 360 * (segment.value / 100); paint.color = segment.color; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), _toRadians(startAngle), _toRadians(sweepAngle), false, paint, ); startAngle += sweepAngle; } } double _toRadians(double degrees) => degrees * 3.1415926 / 180; @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }