|
...
|
...
|
@@ -389,6 +389,9 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
fontSize: 14,
|
|
|
|
color: _getColorByState(state),
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
|
|
fontFamily: '',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
TextSpan(
|
|
...
|
...
|
@@ -412,6 +415,9 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
color: Color(0xFF78787D),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
|
|
fontFamily: '',
|
|
|
|
),
|
|
|
|
),
|
|
|
|
TextSpan(
|
|
...
|
...
|
@@ -430,6 +436,9 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
TextSpan(
|
|
|
|
text: '○ ',
|
|
|
|
style: TextStyle(
|
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
|
|
fontFamily: '',
|
|
|
|
color: _getColorByState(state),
|
|
|
|
// fontWeight: FontWeight.bold,
|
|
|
|
fontSize: 14,
|
|
...
|
...
|
@@ -453,6 +462,9 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
TextSpan(
|
|
|
|
text: '♥ ',
|
|
|
|
style: const TextStyle(
|
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
|
|
fontFamily: '',
|
|
|
|
color: Color(0xFF78787D),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
...
|
...
|
@@ -953,11 +965,58 @@ class _StressBarChartState extends State<_StressBarChart> { |
|
|
|
event is FlPointerHoverEvent ||
|
|
|
|
event is FlPointerEnterEvent) {
|
|
|
|
_stressTooltipTimer?.cancel();
|
|
|
|
if (touchResponse != null &&
|
|
|
|
touchResponse.spot != null) {
|
|
|
|
// 优先用触摸坐标直接定位最近的有数据的柱子
|
|
|
|
// 不依赖 fl_chart 的 spot 检测(touchExtraThreshold 会导致总是找到 spot)
|
|
|
|
final dx = event.localPosition?.dx;
|
|
|
|
if (dx != null && slotStateMap.isNotEmpty) {
|
|
|
|
final stepWidth = barWidth + groupsSpace;
|
|
|
|
final touchedSlotSec =
|
|
|
|
(dx / stepWidth).round() * slotSec;
|
|
|
|
|
|
|
|
final sortedSlots = slotStateMap.keys.toList()
|
|
|
|
..sort();
|
|
|
|
final firstSlot = sortedSlots.first;
|
|
|
|
final lastSlot = sortedSlots.last;
|
|
|
|
|
|
|
|
int? targetIndex;
|
|
|
|
|
|
|
|
if (touchedSlotSec <= firstSlot) {
|
|
|
|
// 超出左侧边界 → 吸附到第一条数据
|
|
|
|
targetIndex = (firstSlot / slotSec)
|
|
|
|
.round()
|
|
|
|
.clamp(0, slotCount - 1);
|
|
|
|
} else if (touchedSlotSec >= lastSlot) {
|
|
|
|
// 超出右侧边界 → 吸附到最后一条数据(最新)
|
|
|
|
targetIndex = (lastSlot / slotSec)
|
|
|
|
.round()
|
|
|
|
.clamp(0, slotCount - 1);
|
|
|
|
} else {
|
|
|
|
// 在数据范围内:仅在距最近数据点 ≤ 5 个槽(30min)时才吸附
|
|
|
|
const snapThresholdSlots = 5;
|
|
|
|
final nearestSlot = sortedSlots.reduce(
|
|
|
|
(a, b) => (a - touchedSlotSec).abs() <
|
|
|
|
(b - touchedSlotSec).abs()
|
|
|
|
? a
|
|
|
|
: b,
|
|
|
|
);
|
|
|
|
final distanceSlots =
|
|
|
|
(nearestSlot - touchedSlotSec).abs() ~/ slotSec;
|
|
|
|
if (distanceSlots <= snapThresholdSlots) {
|
|
|
|
targetIndex = (nearestSlot / slotSec)
|
|
|
|
.round()
|
|
|
|
.clamp(0, slotCount - 1);
|
|
|
|
}
|
|
|
|
// 超过阈值(大片空白区域)→ targetIndex 保持 null,不显示
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
_stressTouchedGroupIndex = targetIndex;
|
|
|
|
});
|
|
|
|
} else if (touchResponse?.spot != null) {
|
|
|
|
// localPosition 不可用时降级到 fl_chart 的 spot
|
|
|
|
setState(() {
|
|
|
|
_stressTouchedGroupIndex =
|
|
|
|
touchResponse.spot!.touchedBarGroupIndex;
|
|
|
|
touchResponse!.spot!.touchedBarGroupIndex;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setState(() {
|
...
|
...
|
|