更新
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
/// ============================================================
|
||||
/// 【SemicircleProgress】半圆正确率进度环
|
||||
/// 1:1 对标 Vue components/lProgressCircle/lProgressCircle.vue
|
||||
/// (report.vue 用法:width 308rpx / height 168rpx / strokeWidth 14 /
|
||||
/// bgColor #D8DFEB / progressColor #1578FF,上半圆从左到右)
|
||||
///
|
||||
/// 尺寸说明(Vue canvas 逻辑像素 → Flutter dp):
|
||||
/// 308rpx = 154.w,168rpx = 84.h,描边 14px = 7.w,
|
||||
/// paddingTop 默认 8px = 4.h;
|
||||
/// radius = min(宽/2 - 描边, 高 - 描边 - paddingTop)
|
||||
/// ============================================================
|
||||
class SemicircleProgress extends StatelessWidget {
|
||||
const SemicircleProgress({
|
||||
super.key,
|
||||
required this.percent,
|
||||
this.width = 154,
|
||||
this.height = 84,
|
||||
this.strokeWidth = 10,
|
||||
this.paddingTop = 4,
|
||||
this.trackColor = const Color(0xFFD8DFEB),
|
||||
this.progressColor = const Color(0xFF1578FF),
|
||||
});
|
||||
|
||||
/// 0 ~ 100
|
||||
final num percent;
|
||||
final double width;
|
||||
final double height;
|
||||
final double strokeWidth;
|
||||
final double paddingTop;
|
||||
final Color trackColor;
|
||||
final Color progressColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final w = width.w;
|
||||
final h = height.h;
|
||||
final sw = strokeWidth.w;
|
||||
|
||||
return SizedBox(
|
||||
width: w,
|
||||
height: h,
|
||||
child: CustomPaint(
|
||||
size: Size(w, h),
|
||||
painter: _SemicirclePainter(
|
||||
percent: percent.toDouble().clamp(0, 100),
|
||||
strokeWidth: sw,
|
||||
paddingTop: paddingTop.h,
|
||||
trackColor: trackColor,
|
||||
progressColor: progressColor,
|
||||
),
|
||||
// 对标 Vue slot:progress-content top 43rpx,居中展示数值
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 31.5.h),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 大数字 + %(font-50 粗体标题色 + font-24 副色 %,底 10rpx)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 5.h),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${percent % 1 == 0 ? percent.toInt() : percent}',
|
||||
style: TextStyle(
|
||||
fontSize: 25.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: const Color(0xFF1D2B47),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '%',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: const Color(0xFF495A7A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'本次正确率',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: const Color(0xFF687FAD),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SemicirclePainter extends CustomPainter {
|
||||
const _SemicirclePainter({
|
||||
required this.percent,
|
||||
required this.strokeWidth,
|
||||
required this.paddingTop,
|
||||
required this.trackColor,
|
||||
required this.progressColor,
|
||||
});
|
||||
|
||||
final double percent;
|
||||
final double strokeWidth;
|
||||
final double paddingTop;
|
||||
final Color trackColor;
|
||||
final Color progressColor;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final halfStroke = strokeWidth / 2;
|
||||
final maxRadiusByWidth = size.width / 2 - strokeWidth;
|
||||
final maxRadiusByHeight = size.height - strokeWidth - paddingTop;
|
||||
final radius = math.max(0.0, math.min(maxRadiusByWidth, maxRadiusByHeight));
|
||||
final centerX = size.width / 2;
|
||||
final centerY = size.height - halfStroke;
|
||||
|
||||
final trackPaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = trackColor;
|
||||
|
||||
// 背景半圆环:π(左)→ 0(右)
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(center: Offset(centerX, centerY), radius: radius),
|
||||
math.pi,
|
||||
math.pi,
|
||||
false,
|
||||
trackPaint,
|
||||
);
|
||||
|
||||
// 进度弧
|
||||
if (percent > 0) {
|
||||
final progressPaint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = progressColor;
|
||||
final sweep = math.pi * (percent / 100);
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(center: Offset(centerX, centerY), radius: radius),
|
||||
math.pi,
|
||||
sweep,
|
||||
false,
|
||||
progressPaint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _SemicirclePainter oldDelegate) {
|
||||
return oldDelegate.percent != percent ||
|
||||
oldDelegate.strokeWidth != strokeWidth ||
|
||||
oldDelegate.trackColor != trackColor ||
|
||||
oldDelegate.progressColor != progressColor;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user