import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; /// ============================================================ /// 【ReciteCircleProgress】圆环进度 —— 对标 Vue l-circle(canvas) /// /// 用在: /// 1. 章节页右上角「总进度」(顺时针,绿色 #1BC26E,底 #EBEBEB) /// 2. 学习小结正确率(Vue :clockwise="false",逆时针绘制,视觉对称) /// /// percent:0~100;中间内容由 child 自定义。 /// ============================================================ class ReciteCircleProgress extends StatelessWidget { const ReciteCircleProgress({ super.key, required this.percent, required this.size, this.strokeWidth = 4, this.color = const Color(0xFF1BC26E), this.trailColor = const Color(0xFFEBEBEB), this.clockwise = true, this.child, }); final double percent; final double size; // 直径 final double strokeWidth; final Color color; final Color trailColor; /// false = 逆时针起笔(对标 l-circle :clockwise="false") final bool clockwise; final Widget? child; @override Widget build(BuildContext context) { return SizedBox( width: size.w, height: size.w, child: CustomPaint( painter: _CirclePainter( percent: percent.clamp(0, 100).toDouble(), strokeWidth: strokeWidth.w, color: color, trailColor: trailColor, clockwise: clockwise, ), child: Center(child: child), ), ); } } class _CirclePainter extends CustomPainter { _CirclePainter({ required this.percent, required this.strokeWidth, required this.color, required this.trailColor, required this.clockwise, }); final double percent; final double strokeWidth; final Color color; final Color trailColor; final bool clockwise; @override void paint(Canvas canvas, Size size) { final center = Offset(size.width / 2, size.height / 2); final radius = (size.width - strokeWidth) / 2; final trackPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..color = trailColor; final progressPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round ..color = color; canvas.drawCircle(center, radius, trackPaint); const start = -math.pi / 2; // 12 点方向 final sweep = 2 * math.pi * percent / 100; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), clockwise ? start : start - sweep, sweep, false, progressPaint, ); } @override bool shouldRepaint(covariant _CirclePainter old) => old.percent != percent || old.strokeWidth != strokeWidth || old.color != color || old.clockwise != clockwise; }