| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- 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<PieData> 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<double>(
- tween: Tween<double>(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<PieData> 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;
- }
|