大小样式调整
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||
import 'package:flutter/animation.dart';
|
||||
|
||||
// 占位导入(请替换为你项目的实际路径)
|
||||
import '../../../../core/app/app_user_cubit.dart';
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../core/network/net_message_dispatcher.dart';
|
||||
@@ -120,8 +121,8 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
return SizedBox(height: height);
|
||||
}
|
||||
|
||||
// ====================== 图表卡片(统一样式) ======================
|
||||
Widget _chartCard({required String title, required Widget child}) {
|
||||
// ====================== 图表卡片(统一样式:标题字体缩小+标题与切换按钮同行) ======================
|
||||
Widget _chartCard({required String title, required Widget child, required bool isGaugeMode, required Function() onGaugeTap, required Function() onChartTap}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
@@ -132,9 +133,46 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF1677FF)),
|
||||
// 标题与切换按钮同行布局
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16, // 标题字体缩小(原18)
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF1677FF),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onGaugeTap,
|
||||
child: Text(
|
||||
"仪表盘",
|
||||
style: TextStyle(
|
||||
color: isGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: isGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
GestureDetector(
|
||||
onTap: onChartTap,
|
||||
child: Text(
|
||||
"折线图",
|
||||
style: TextStyle(
|
||||
color: !isGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: !isGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
child,
|
||||
@@ -143,38 +181,37 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
);
|
||||
}
|
||||
|
||||
// ====================== 带动画的圆形仪表盘(核心修改) ======================
|
||||
// ====================== 带动画的圆形仪表盘(尺寸缩小:原220→180,绘制尺寸280→240) ======================
|
||||
Widget _circularGauge({
|
||||
required double value,
|
||||
required double maxValue,
|
||||
required String unit,
|
||||
required String label,
|
||||
List<double> majorTicks = const [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
|
||||
}) {
|
||||
return AnimatedCircularGauge(value: value, maxValue: maxValue, unit: unit, majorTicks: majorTicks);
|
||||
}
|
||||
|
||||
// ====================== 罗盘式航向角仪表盘 ======================
|
||||
// ====================== 罗盘式航向角仪表盘(尺寸同步缩小) ======================
|
||||
Widget _compassGauge({required double yaw}) {
|
||||
return SizedBox(
|
||||
height: 250,
|
||||
height: 200, // 原250→200
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CustomPaint(
|
||||
painter: CompassGaugePainter(yaw: yaw),
|
||||
size: const Size(280, 280),
|
||||
size: const Size(240, 240), // 原280→240
|
||||
),
|
||||
Text(
|
||||
"${yaw.toStringAsFixed(0)}°",
|
||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black),
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black), // 字体同步缩小
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ====================== 折线图(统一样式) ======================
|
||||
// ====================== 折线图(修复溢出:限制宽度+裁剪+适配父容器) ======================
|
||||
Widget _styledLineChart({
|
||||
required String title,
|
||||
required List<LineChartBarData> lines,
|
||||
@@ -202,67 +239,69 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
// 用Expanded适配宽度,避免溢出
|
||||
// 修复溢出:用ConstrainedBox限制宽度+ClipRect裁剪+Expanded适配
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: yAxisMin,
|
||||
maxY: yAxisMax,
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
horizontalInterval: (yAxisMax - yAxisMin) / leftTicks.length,
|
||||
getDrawingHorizontalLine: (value) => const FlLine(color: Color(0xFFE5E5E5), strokeWidth: 1),
|
||||
drawVerticalLine: false,
|
||||
),
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: (yAxisMax - yAxisMin) / leftTicks.length,
|
||||
getTitlesWidget: (value, meta) {
|
||||
if (leftTicks.contains(value)) {
|
||||
return Text(value.toStringAsFixed(0), style: const TextStyle(fontSize: 12, color: Colors.grey));
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
// 限制左侧标签宽度
|
||||
reservedSize: 40,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: double.infinity),
|
||||
child: ClipRect(
|
||||
child: SizedBox(
|
||||
height: 180, // 原200→180,缩小折线图高度
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: yAxisMin,
|
||||
maxY: yAxisMax,
|
||||
minX: 0,
|
||||
maxX: bottomLabels.length - 1.toDouble(), // 固定X轴范围,防止溢出
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
horizontalInterval: (yAxisMax - yAxisMin) / leftTicks.length,
|
||||
getDrawingHorizontalLine: (value) => const FlLine(color: Color(0xFFE5E5E5), strokeWidth: 1),
|
||||
drawVerticalLine: false,
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: bottomInterval,
|
||||
// 限制底部标签高度和宽度
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (value, meta) {
|
||||
int idx = value.toInt();
|
||||
if (idx >= 0 && idx < bottomLabels.length) {
|
||||
return Container(
|
||||
width: 40,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
bottomLabels[idx],
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: (yAxisMax - yAxisMin) / leftTicks.length,
|
||||
getTitlesWidget: (value, meta) {
|
||||
if (leftTicks.contains(value)) {
|
||||
return Text(value.toStringAsFixed(0), style: const TextStyle(fontSize: 12, color: Colors.grey));
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
reservedSize: 40,
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
interval: bottomInterval,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (value, meta) {
|
||||
int idx = value.toInt();
|
||||
if (idx >= 0 && idx < bottomLabels.length) {
|
||||
return Container(
|
||||
width: 40,
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
bottomLabels[idx],
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
lineBarsData: lines,
|
||||
extraLinesData: ExtraLinesData(horizontalLines: []),
|
||||
),
|
||||
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
lineBarsData: lines,
|
||||
// 设置图表边距,避免内容贴边
|
||||
minX: 0,
|
||||
maxX: bottomLabels.length - 1.toDouble(),
|
||||
extraLinesData: ExtraLinesData(horizontalLines: []),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -316,240 +355,217 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
// 电压仪表盘/折线图
|
||||
// 电压仪表盘/折线图(使用新的chartCard,传入切换回调)
|
||||
_chartCard(
|
||||
title: "电压 (V)",
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _voltageGaugeMode = true),
|
||||
child: Text(
|
||||
"仪表盘",
|
||||
style: TextStyle(
|
||||
color: _voltageGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: _voltageGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
isGaugeMode: _voltageGaugeMode,
|
||||
onGaugeTap: () => setState(() => _voltageGaugeMode = true),
|
||||
onChartTap: () => setState(() => _voltageGaugeMode = false),
|
||||
child: _voltageGaugeMode
|
||||
? _circularGauge(value: status.voltage, maxValue: 250, unit: "V", majorTicks: const [0, 50, 100, 150, 200, 250])
|
||||
: SizedBox(
|
||||
height: 180, // 原200→180
|
||||
child: _styledLineChart(
|
||||
title: "电压",
|
||||
lines: [_lineData(_voltageHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 250,
|
||||
leftTicks: const [0, 50, 100, 150, 200, 250],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _voltageGaugeMode = false),
|
||||
child: Text(
|
||||
"折线图",
|
||||
style: TextStyle(
|
||||
color: !_voltageGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: !_voltageGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_voltageGaugeMode
|
||||
? _circularGauge(value: status.voltage, maxValue: 250, unit: "V", label: "电压", majorTicks: const [0, 50, 100, 150, 200, 250])
|
||||
: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "电压",
|
||||
lines: [_lineData(_voltageHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 250,
|
||||
leftTicks: const [0, 50, 100, 150, 200, 250],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
|
||||
// 芯片温度仪表盘/折线图
|
||||
_chartCard(
|
||||
title: "芯片温度 (°C)",
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _chipTempGaugeMode = true),
|
||||
child: Text(
|
||||
"仪表盘",
|
||||
style: TextStyle(
|
||||
color: _chipTempGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: _chipTempGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
isGaugeMode: _chipTempGaugeMode,
|
||||
onGaugeTap: () => setState(() => _chipTempGaugeMode = true),
|
||||
onChartTap: () => setState(() => _chipTempGaugeMode = false),
|
||||
child: _chipTempGaugeMode
|
||||
? _circularGauge(value: status.chipTemp, maxValue: 100, unit: "°C", majorTicks: const [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||
: SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "芯片温度",
|
||||
lines: [_lineData(_chipTempHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _chipTempGaugeMode = false),
|
||||
child: Text(
|
||||
"折线图",
|
||||
style: TextStyle(
|
||||
color: !_chipTempGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: !_chipTempGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_chipTempGaugeMode
|
||||
? _circularGauge(
|
||||
value: status.chipTemp,
|
||||
maxValue: 100,
|
||||
unit: "°C",
|
||||
label: "芯片温度",
|
||||
majorTicks: const [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
|
||||
)
|
||||
: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "芯片温度",
|
||||
lines: [_lineData(_chipTempHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
|
||||
// 割刀速度仪表盘/折线图
|
||||
_chartCard(
|
||||
title: "割刀速度 (rpm)",
|
||||
isGaugeMode: _knifeSpeedGaugeMode,
|
||||
onGaugeTap: () => setState(() => _knifeSpeedGaugeMode = true),
|
||||
onChartTap: () => setState(() => _knifeSpeedGaugeMode = false),
|
||||
child: _knifeSpeedGaugeMode
|
||||
? _circularGauge(
|
||||
value: double.tryParse(status.knifeCuttingSpeed) ?? 0,
|
||||
maxValue: 3000,
|
||||
unit: "rpm",
|
||||
majorTicks: const [0, 500, 1000, 1500, 2000, 2500, 3000],
|
||||
)
|
||||
: SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "割刀速度",
|
||||
lines: [_lineData(_knifeHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
|
||||
// 测量速度对比(无切换按钮,使用原布局+标题字体缩小)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _knifeSpeedGaugeMode = true),
|
||||
child: Text(
|
||||
"仪表盘",
|
||||
style: TextStyle(
|
||||
color: _knifeSpeedGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: _knifeSpeedGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _knifeSpeedGaugeMode = false),
|
||||
child: Text(
|
||||
"折线图",
|
||||
style: TextStyle(
|
||||
color: !_knifeSpeedGaugeMode ? const Color(0xFF1677FF) : Colors.grey,
|
||||
fontSize: 14,
|
||||
fontWeight: !_knifeSpeedGaugeMode ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
"左右轮测量速度对比 (rpm)",
|
||||
style: const TextStyle(
|
||||
fontSize: 16, // 标题字体缩小
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF1677FF),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_knifeSpeedGaugeMode
|
||||
? _circularGauge(
|
||||
value: double.tryParse(status.knifeCuttingSpeed) ?? 0,
|
||||
maxValue: 3000,
|
||||
unit: "rpm",
|
||||
label: "割刀速度",
|
||||
majorTicks: const [0, 500, 1000, 1500, 2000, 2500, 3000],
|
||||
)
|
||||
: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "割刀速度",
|
||||
lines: [_lineData(_knifeHistory, const Color(0xFF4CAF50))],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "左右轮测量速度对比",
|
||||
lines: [_lineData(_leftMeasureHistory, const Color(0xFF3F51B5)), _lineData(_rightMeasureHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
// 测量速度对比(左轮 + 右轮在一张图)
|
||||
_chartCard(
|
||||
title: "左右轮测量速度对比 (rpm)",
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "左右轮测量速度对比",
|
||||
lines: [
|
||||
_lineData(_leftMeasureHistory, const Color(0xFF3F51B5)), // 左轮
|
||||
_lineData(_rightMeasureHistory, const Color(0xFF8BC34A)), // 右轮
|
||||
],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
|
||||
// 目标速度对比
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"左右轮目标速度对比 (rpm)",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1677FF)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "左右轮目标速度对比",
|
||||
lines: [_lineData(_leftTargetHistory, const Color(0xFF3F51B5)), _lineData(_rightTargetHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
_gap(),
|
||||
// 目标速度对比(左轮 + 右轮在一张图)
|
||||
_chartCard(
|
||||
title: "左右轮目标速度对比 (rpm)",
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "左右轮目标速度对比",
|
||||
lines: [
|
||||
_lineData(_leftTargetHistory, const Color(0xFF3F51B5)), // 左轮
|
||||
_lineData(_rightTargetHistory, const Color(0xFF8BC34A)), // 右轮
|
||||
],
|
||||
yAxisMax: 3000,
|
||||
yAxisMin: -3000,
|
||||
leftTicks: const [-3000, -2000, -1000, 0, 1000, 2000, 3000],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
_gap(),
|
||||
|
||||
// 电流对比
|
||||
_chartCard(
|
||||
title: "电流对比 (A)",
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "电流对比",
|
||||
lines: [_lineData(_leftCurrentHistory, const Color(0xFF3F51B5)), _lineData(_rightCurrentHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"电流对比 (A)",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1677FF)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "电流对比",
|
||||
lines: [_lineData(_leftCurrentHistory, const Color(0xFF3F51B5)), _lineData(_rightCurrentHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
|
||||
// 电机温度对比
|
||||
_chartCard(
|
||||
title: "电机温度对比 (°C)",
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _styledLineChart(
|
||||
title: "电机温度对比",
|
||||
lines: [_lineData(_leftTempHistory, const Color(0xFF3F51B5)), _lineData(_rightTempHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"电机温度对比 (°C)",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1677FF)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: 180,
|
||||
child: _styledLineChart(
|
||||
title: "电机温度对比",
|
||||
lines: [_lineData(_leftTempHistory, const Color(0xFF3F51B5)), _lineData(_rightTempHistory, const Color(0xFF8BC34A))],
|
||||
yAxisMax: 100,
|
||||
leftTicks: const [0, 20, 40, 60, 80, 100],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
|
||||
// 航向角仪表盘
|
||||
_chartCard(
|
||||
title: "航向角 (°)",
|
||||
child: _compassGauge(yaw: status.yaw),
|
||||
// 航向角仪表盘(标题字体缩小+尺寸缩小)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"航向角 (°)",
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF1677FF)),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_compassGauge(yaw: status.yaw),
|
||||
],
|
||||
),
|
||||
),
|
||||
_gap(),
|
||||
],
|
||||
@@ -580,7 +596,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Text("电机参数", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
child: Text("电机参数", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), // 标题字体缩小
|
||||
),
|
||||
Table(
|
||||
border: TableBorder.all(color: const Color(0xFFE5E5E5)),
|
||||
@@ -631,7 +647,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Text("其他参数", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
child: Text("其他参数", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), // 标题字体缩小
|
||||
),
|
||||
Table(
|
||||
border: TableBorder.all(color: const Color(0xFFE5E5E5)),
|
||||
@@ -831,7 +847,7 @@ class _RunningStatusPageState extends State<RunningStatusPage> {
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 带动画的圆形仪表盘 Widget ======================
|
||||
// ====================== 带动画的圆形仪表盘 Widget(尺寸缩小:原220→180,绘制尺寸280→240) ======================
|
||||
class AnimatedCircularGauge extends StatefulWidget {
|
||||
final double value;
|
||||
final double maxValue;
|
||||
@@ -852,64 +868,56 @@ class _AnimatedCircularGaugeState extends State<AnimatedCircularGauge> with Sing
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 初始化动画控制器,时长800ms(可调整)
|
||||
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 800));
|
||||
// 初始值动画
|
||||
_previousValue = widget.value;
|
||||
_valueAnimation =
|
||||
Tween<double>(begin: 0, end: widget.value).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.easeOutCubic, // 缓出曲线,动画更自然
|
||||
),
|
||||
)..addListener(() {
|
||||
setState(() {}); // 动画值变化时刷新UI
|
||||
});
|
||||
_valueAnimation = Tween<double>(begin: 0, end: widget.value).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic))
|
||||
..addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
_animationController.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant AnimatedCircularGauge oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
// 数值变化时触发新的动画
|
||||
if (widget.value != oldWidget.value) {
|
||||
_previousValue = _valueAnimation.value; // 从当前值开始动画
|
||||
_previousValue = _valueAnimation.value;
|
||||
_valueAnimation =
|
||||
Tween<double>(begin: _previousValue, end: widget.value).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeOutCubic))
|
||||
..addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
_animationController.reset(); // 重置动画
|
||||
_animationController.forward(); // 执行动画
|
||||
_animationController.reset();
|
||||
_animationController.forward();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose(); // 释放动画控制器
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 220,
|
||||
height: 180, // 原220→180
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CustomPaint(
|
||||
painter: CircularGaugePainter(
|
||||
value: _valueAnimation.value, // 使用动画插值后的数值
|
||||
maxValue: widget.maxValue,
|
||||
majorTicks: widget.majorTicks,
|
||||
),
|
||||
size: const Size(280, 280),
|
||||
painter: CircularGaugePainter(value: _valueAnimation.value, maxValue: widget.maxValue, majorTicks: widget.majorTicks),
|
||||
size: const Size(240, 240), // 原280→240
|
||||
),
|
||||
Positioned(
|
||||
bottom: 20,
|
||||
child: Text(
|
||||
"${_valueAnimation.value.toStringAsFixed(2)} ${widget.unit}",
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500, color: Colors.grey),
|
||||
style: const TextStyle(
|
||||
fontSize: 18, // 原20→18
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -918,22 +926,20 @@ class _AnimatedCircularGaugeState extends State<AnimatedCircularGauge> with Sing
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 圆形仪表盘绘制器(带动画支持) ======================
|
||||
// ====================== 圆形仪表盘绘制器(修复widget关键字错误) ======================
|
||||
// ====================== 圆形仪表盘绘制器(尺寸同步缩小) ======================
|
||||
class CircularGaugePainter extends CustomPainter {
|
||||
final double value;
|
||||
final double maxValue;
|
||||
final List<double> majorTicks; // 直接通过成员变量获取,而非widget
|
||||
final List<double> 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 / 2);
|
||||
final radius = size.width / 2 - 20;
|
||||
|
||||
// 1. 绘制背景圆弧(浅蓝底色,270°范围:从135°到405°,对应左下到右下的半圆)
|
||||
// 背景圆弧
|
||||
final bgArcPaint = Paint()
|
||||
..color = const Color(0xFFE3F2FD)
|
||||
..style = PaintingStyle.stroke
|
||||
@@ -942,8 +948,8 @@ class CircularGaugePainter extends CustomPainter {
|
||||
final bgRect = Rect.fromCircle(center: center, radius: radius);
|
||||
canvas.drawArc(bgRect, 135 * math.pi / 180, 270 * math.pi / 180, false, bgArcPaint);
|
||||
|
||||
// 2. 绘制进度圆弧(浅蓝色,根据动画后的数值比例绘制)
|
||||
final progressRatio = math.min(value / maxValue, 1.0); // 限制最大为1
|
||||
// 进度圆弧
|
||||
final progressRatio = math.min(value / maxValue, 1.0);
|
||||
final progressPaint = Paint()
|
||||
..color = const Color(0xFFBBDEFB)
|
||||
..style = PaintingStyle.stroke
|
||||
@@ -951,42 +957,38 @@ class CircularGaugePainter extends CustomPainter {
|
||||
..strokeCap = StrokeCap.round;
|
||||
canvas.drawArc(bgRect, 135 * math.pi / 180, 270 * math.pi / 180 * progressRatio, false, progressPaint);
|
||||
|
||||
// 3. 绘制刻度(主刻度+副刻度)
|
||||
// 刻度
|
||||
final tickPaint = Paint()..color = Colors.grey.shade400;
|
||||
final majorTickPaint = Paint()
|
||||
..color = Colors.grey.shade600
|
||||
..strokeWidth = 2;
|
||||
|
||||
// 总刻度数(每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;
|
||||
|
||||
// 副刻度(短刻度)
|
||||
tickPaint.strokeWidth = 1;
|
||||
canvas.drawLine(tickStart, tickEnd, tickPaint);
|
||||
|
||||
// 主刻度(长刻度,匹配majorTicks的数值)
|
||||
final tickValue = (i / totalTicks) * maxValue;
|
||||
// 修复:直接使用this.majorTicks(去掉widget.)
|
||||
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: 14, color: Colors.grey, fontWeight: FontWeight.w500),
|
||||
style: const TextStyle(
|
||||
fontSize: 12, // 原14→12,刻度数值缩小
|
||||
color: Colors.grey,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
// 数值位置(在主刻度外侧)
|
||||
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;
|
||||
@@ -994,38 +996,30 @@ class CircularGaugePainter extends CustomPainter {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 计算指针角度(使用动画后的数值)
|
||||
// 指针角度
|
||||
final needleAngle = 135 * math.pi / 180 + 270 * math.pi / 180 * progressRatio;
|
||||
|
||||
// 5. 绘制三角形指针(细尖角指向刻度,宽底边在中心)
|
||||
// 三角形指针
|
||||
final needlePaint = Paint()
|
||||
..color = Colors.grey.shade600
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
// 调整三角形顶点和底边位置:细尖角指向刻度,宽底边在中心
|
||||
final needlePath = Path()
|
||||
// 1. 移动画笔到指针的细尖角(指向刻度的一端)
|
||||
..moveTo(center.dx + (radius - 10) * math.cos(needleAngle), center.dy + (radius - 10) * math.sin(needleAngle))
|
||||
// 2. 绘制第一条边:从尖角到中心左侧的底边端点
|
||||
..lineTo(
|
||||
center.dx + 8 * math.cos(needleAngle - math.pi / 2), // 中心左侧8px
|
||||
center.dy + 8 * math.sin(needleAngle - math.pi / 2),
|
||||
center.dx + 6 * math.cos(needleAngle - math.pi / 2), // 原8→6,指针底边宽度缩小
|
||||
center.dy + 6 * math.sin(needleAngle - math.pi / 2),
|
||||
)
|
||||
// 3. 绘制第二条边:从中心左侧端点到中心右侧的底边端点
|
||||
..lineTo(
|
||||
center.dx + 8 * math.cos(needleAngle + math.pi / 2), // 中心右侧8px
|
||||
center.dy + 8 * math.sin(needleAngle + math.pi / 2),
|
||||
)
|
||||
// 4. 闭合路径:回到尖角,形成三角形
|
||||
..lineTo(center.dx + 6 * math.cos(needleAngle + math.pi / 2), center.dy + 6 * math.sin(needleAngle + math.pi / 2))
|
||||
..close();
|
||||
|
||||
canvas.drawPath(needlePath, needlePaint);
|
||||
|
||||
// 6. 绘制指针中心圆点(增强视觉效果)
|
||||
// 中心圆点
|
||||
final centerDotPaint = Paint()
|
||||
..color = Colors.white
|
||||
..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(center, 4, centerDotPaint);
|
||||
canvas.drawCircle(center, 3, centerDotPaint); // 原4→3,圆点缩小
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1034,7 +1028,7 @@ class CircularGaugePainter extends CustomPainter {
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== 罗盘式航向角仪表盘绘制器 ======================
|
||||
// ====================== 罗盘式航向角仪表盘绘制器(尺寸缩小) ======================
|
||||
class CompassGaugePainter extends CustomPainter {
|
||||
final double yaw;
|
||||
|
||||
@@ -1049,8 +1043,7 @@ class CompassGaugePainter extends CustomPainter {
|
||||
final outerPaint = Paint()
|
||||
..color = const Color(0xFF1677FF)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 12;
|
||||
|
||||
..strokeWidth = 10; // 原12→10,外圈宽度缩小
|
||||
canvas.drawCircle(center, radius, outerPaint);
|
||||
|
||||
// 刻度
|
||||
@@ -1066,13 +1059,11 @@ class CompassGaugePainter extends CustomPainter {
|
||||
final y2 = center.dy + radius * math.sin(angle);
|
||||
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), tickPaint);
|
||||
|
||||
// 主刻度(45°间隔)
|
||||
if (i % 9 == 0) {
|
||||
final x3 = center.dx + (radius - 20) * math.cos(angle);
|
||||
final y3 = center.dy + (radius - 20) * math.sin(angle);
|
||||
canvas.drawLine(Offset(x1, y1), Offset(x3, y3), tickPaint..strokeWidth = 4);
|
||||
|
||||
// 方向文字
|
||||
final direction = i == 0
|
||||
? "N"
|
||||
: i == 9
|
||||
@@ -1086,7 +1077,7 @@ class CompassGaugePainter extends CustomPainter {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: direction,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black),
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black), // 原18→16
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
@@ -1095,12 +1086,11 @@ class CompassGaugePainter extends CustomPainter {
|
||||
textPainter.paint(canvas, Offset(textX, textY));
|
||||
}
|
||||
|
||||
// 角度值
|
||||
final angleValue = (i * 10).toString();
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: angleValue,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black54),
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black54), // 原14→12
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
Reference in New Issue
Block a user