仪表盘 样式优化

This commit is contained in:
mmc
2026-03-04 13:26:56 +08:00
parent 5194c9e796
commit 2aa5846308

View File

@@ -142,13 +142,13 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
);
}
// ====================== 大半个圆仪表盘(电压/芯片温度) ======================
Widget _largeArcGauge({
// ====================== 圆形仪表盘(匹配目标样式) ======================
Widget _circularGauge({
required double value,
required double maxValue,
required String unit,
required String label,
List<double> majorTicks = const [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250],
List<double> majorTicks = const [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
}) {
return SizedBox(
height: 220,
@@ -156,14 +156,14 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
alignment: Alignment.center,
children: [
CustomPaint(
painter: LargeArcGaugePainter(value: value / maxValue, maxValue: maxValue, majorTicks: majorTicks),
size: const Size(300, 180),
painter: CircularGaugePainter(value: value, maxValue: maxValue, majorTicks: majorTicks),
size: const Size(280, 280),
),
Positioned(
bottom: 10,
bottom: 20,
child: Text(
"${value.toStringAsFixed(2)} $unit",
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.grey),
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: Colors.grey),
),
),
],
@@ -368,13 +368,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
),
const SizedBox(height: 16),
_voltageGaugeMode
? _largeArcGauge(
value: status.voltage,
maxValue: 250,
unit: "V",
label: "电压",
majorTicks: const [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250],
)
? _circularGauge(value: status.voltage, maxValue: 250, unit: "V", label: "电压", majorTicks: const [0, 50, 100, 150, 200, 250])
: SizedBox(
height: 200,
child: _styledLineChart(
@@ -424,7 +418,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
),
const SizedBox(height: 16),
_chipTempGaugeMode
? _largeArcGauge(
? _circularGauge(
value: status.chipTemp,
maxValue: 100,
unit: "°C",
@@ -480,7 +474,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
),
const SizedBox(height: 16),
_knifeSpeedGaugeMode
? _largeArcGauge(
? _circularGauge(
value: double.tryParse(status.knifeCuttingSpeed) ?? 0,
maxValue: 3000,
unit: "rpm",
@@ -854,88 +848,105 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
}
}
// ====================== 大半个圆仪表盘绘制器 ======================
class LargeArcGaugePainter extends CustomPainter {
// ====================== 圆形仪表盘绘制器(匹配目标样式) ======================
class CircularGaugePainter extends CustomPainter {
final double value;
final double maxValue;
final List<double> majorTicks;
LargeArcGaugePainter({required this.value, required this.maxValue, required this.majorTicks});
CircularGaugePainter({required this.value, required this.maxValue, required this.majorTicks});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height);
// 画布中心和半径(留出边距)
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2 - 20;
// 背景弧(大半个圆,从 -135° 到 135°)
final bgPaint = Paint()
// 1. 绘制背景圆弧(浅蓝底色,270°范围:从135°到405°,对应左下到右下的半圆)
final bgArcPaint = Paint()
..color = const Color(0xFFE3F2FD)
..style = PaintingStyle.stroke
..strokeWidth = 20
..strokeWidth = 8
..strokeCap = StrokeCap.round;
final bgRect = Rect.fromCircle(center: center, radius: radius);
canvas.drawArc(bgRect, 135 * math.pi / 180, 270 * math.pi / 180, false, bgArcPaint);
canvas.drawArc(Rect.fromCircle(center: center, radius: radius), -135 * math.pi / 180, 270 * math.pi / 180, false, bgPaint);
// 进度弧
// 2. 绘制进度圆弧(浅蓝色,根据数值比例绘制)
final progressRatio = math.min(value / maxValue, 1.0); // 限制最大为1
final progressPaint = Paint()
..color = const Color(0xFFBBDEFB)
..style = PaintingStyle.stroke
..strokeWidth = 20
..strokeWidth = 8
..strokeCap = StrokeCap.round;
canvas.drawArc(bgRect, 135 * math.pi / 180, 270 * math.pi / 180 * progressRatio, false, progressPaint);
canvas.drawArc(Rect.fromCircle(center: center, radius: radius), -135 * math.pi / 180, 270 * math.pi / 180 * value, false, progressPaint);
// 刻度
final tickPaint = Paint()
..color = Colors.black54
// 3. 绘制刻度(主刻度+副刻度)
final tickPaint = Paint()..color = Colors.grey.shade400;
final majorTickPaint = Paint()
..color = Colors.grey.shade600
..strokeWidth = 2;
for (int i = 0; i <= 45; i++) {
final angle = -135 * math.pi / 180 + (270 * math.pi / 180) * (i / 45);
final x1 = center.dx + (radius - 15) * math.cos(angle);
final y1 = center.dy + (radius - 15) * math.sin(angle);
final x2 = center.dx + radius * math.cos(angle);
final y2 = center.dy + radius * math.sin(angle);
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), tickPaint);
// 总刻度数(每1°一个副刻度,每10°一个主刻度)
const totalTicks = 270;
for (int i = 0; i < totalTicks; i++) {
// 计算刻度角度(从135°开始,到405°结束)
final angle = 135 * math.pi / 180 + (i * math.pi * 270 / 180) / totalTicks;
// 刻度起始和结束位置
final tickStart = center + Offset(math.cos(angle), math.sin(angle)) * (radius - 4);
final tickEnd = center + Offset(math.cos(angle), math.sin(angle)) * radius;
// 主刻度
if (majorTicks.contains(i * (maxValue / 45))) {
final x3 = center.dx + (radius - 25) * math.cos(angle);
final y3 = center.dy + (radius - 25) * math.sin(angle);
canvas.drawLine(Offset(x1, y1), Offset(x3, y3), tickPaint..strokeWidth = 4);
// 副刻度(短刻度)
tickPaint.strokeWidth = 1;
canvas.drawLine(tickStart, tickEnd, tickPaint);
// 刻度值
final textValue = (i * (maxValue / 45)).toStringAsFixed(0);
// 主刻度(长刻度,匹配majorTicks的数值)
final tickValue = (i / totalTicks) * maxValue;
if (majorTicks.any((tick) => (tickValue - tick).abs() < 0.1)) {
final majorTickStart = center + Offset(math.cos(angle), math.sin(angle)) * (radius - 8);
canvas.drawLine(majorTickStart, tickEnd, majorTickPaint);
// 绘制主刻度数值
final textValue = tickValue.toStringAsFixed(0);
final textPainter = TextPainter(
text: TextSpan(
text: textValue,
style: const TextStyle(fontSize: 16, color: Colors.black54),
style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.w500),
),
textDirection: TextDirection.ltr,
)..layout();
final textX = center.dx + (radius - 40) * math.cos(angle) - textPainter.width / 2;
final textY = center.dy + (radius - 40) * math.sin(angle) - textPainter.height / 2;
// 数值位置(在主刻度外侧)
final textOffset = center + Offset(math.cos(angle), math.sin(angle)) * (radius - 20);
final textX = textOffset.dx - textPainter.width / 2;
final textY = textOffset.dy - textPainter.height / 2;
textPainter.paint(canvas, Offset(textX, textY));
}
}
// 指针(修正方向)
final needleAngle = -135 * math.pi / 180 + 270 * math.pi / 180 * value;
// 4. 绘制指针
final needleAngle = 135 * math.pi / 180 + 270 * math.pi / 180 * progressRatio;
final needlePaint = Paint()
..color = const Color(0xFF90A4AE)
..color = Colors.grey.shade400
..style = PaintingStyle.fill;
// 指针形状(三角形)
final needlePath = Path()
..moveTo(center.dx, center.dy)
..lineTo(center.dx + radius * 0.8 * math.cos(needleAngle - 0.03), center.dy + radius * 0.8 * math.sin(needleAngle - 0.03))
..lineTo(center.dx + radius * 0.8 * math.cos(needleAngle + 0.03), center.dy + radius * 0.8 * math.sin(needleAngle + 0.03))
..lineTo(center.dx + (radius - 20) * math.cos(needleAngle - 0.02), center.dy + (radius - 20) * math.sin(needleAngle - 0.02))
..lineTo(center.dx + (radius - 20) * math.cos(needleAngle + 0.02), center.dy + (radius - 20) * math.sin(needleAngle + 0.02))
..close();
canvas.drawPath(needlePath, needlePaint);
// 5. 绘制指针中心圆点
final centerDotPaint = Paint()
..color = Colors.grey.shade400
..style = PaintingStyle.fill;
canvas.drawCircle(center, 6, centerDotPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
bool shouldRepaint(covariant CircularGaugePainter oldDelegate) {
return oldDelegate.value != value || oldDelegate.maxValue != maxValue;
}
}
// ====================== 罗盘式航向角仪表盘绘制器 ======================