|
...
|
...
|
@@ -905,6 +905,9 @@ class _BedtimeChart extends StatefulWidget { |
|
|
|
}
|
|
|
|
|
|
|
|
class _BedtimeChartState extends State<_BedtimeChart> {
|
|
|
|
static const _xAxisLeftInset = 10.0;
|
|
|
|
static const _xAxisRightInset = 40.0;
|
|
|
|
|
|
|
|
int? _selectedDayIndex;
|
|
|
|
Timer? _tooltipTimer;
|
|
|
|
|
|
...
|
...
|
@@ -985,12 +988,12 @@ class _BedtimeChartState extends State<_BedtimeChart> { |
|
|
|
}
|
|
|
|
|
|
|
|
double _lineXForDay(double width, int dayIndex) {
|
|
|
|
return _barCenterForIndex(
|
|
|
|
dayIndex,
|
|
|
|
width,
|
|
|
|
widget.days.length,
|
|
|
|
widget.compact,
|
|
|
|
);
|
|
|
|
final count = widget.days.length;
|
|
|
|
if (count <= 1 || width <= _xAxisLeftInset + _xAxisRightInset) {
|
|
|
|
return width / 2;
|
|
|
|
}
|
|
|
|
final dataWidth = width - _xAxisLeftInset - _xAxisRightInset;
|
|
|
|
return _xAxisLeftInset + dataWidth * dayIndex / (count - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
LineChartData _chartData(
|
|
...
|
...
|
@@ -1044,18 +1047,11 @@ class _BedtimeChartState extends State<_BedtimeChart> { |
|
|
|
}
|
|
|
|
|
|
|
|
final count = widget.days.length;
|
|
|
|
final dataMaxX = count <= 1 ? 0.0 : (count - 1).toDouble();
|
|
|
|
final horizontalInset = count <= 1
|
|
|
|
? chartWidth / 2
|
|
|
|
: _barCenterForIndex(0, chartWidth, count, widget.compact);
|
|
|
|
final dataWidth = chartWidth - horizontalInset * 2;
|
|
|
|
final xPadding = count <= 1 || dataWidth <= 0
|
|
|
|
? 0.5
|
|
|
|
: horizontalInset * (count - 1) / dataWidth;
|
|
|
|
final xBounds = _xBounds(chartWidth, count);
|
|
|
|
|
|
|
|
return LineChartData(
|
|
|
|
minX: -xPadding,
|
|
|
|
maxX: dataMaxX + xPadding,
|
|
|
|
minX: xBounds.minX,
|
|
|
|
maxX: xBounds.maxX,
|
|
|
|
minY: 0,
|
|
|
|
maxY: _tooltipChartMaxY,
|
|
|
|
gridData: const FlGridData(show: false),
|
|
...
|
...
|
@@ -1084,12 +1080,7 @@ class _BedtimeChartState extends State<_BedtimeChart> { |
|
|
|
|
|
|
|
void _selectSpotAt(double dx, double chartWidth) {
|
|
|
|
_tooltipTimer?.cancel();
|
|
|
|
final index = _barIndexForTouch(
|
|
|
|
dx,
|
|
|
|
chartWidth,
|
|
|
|
widget.days.length,
|
|
|
|
widget.compact,
|
|
|
|
);
|
|
|
|
final index = _dayIndexForTouch(dx, chartWidth);
|
|
|
|
final hasData = index != null &&
|
|
|
|
widget.days[index].hasSleepData &&
|
|
|
|
widget.days[index].heartRate.sleepStart != null;
|
|
...
|
...
|
@@ -1098,6 +1089,38 @@ class _BedtimeChartState extends State<_BedtimeChart> { |
|
|
|
setState(() => _selectedDayIndex = nextIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
({double minX, double maxX}) _xBounds(double width, int count) {
|
|
|
|
if (count <= 1 || width <= _xAxisLeftInset + _xAxisRightInset) {
|
|
|
|
return (minX: -0.5, maxX: 0.5);
|
|
|
|
}
|
|
|
|
final scale = (count - 1) / (width - _xAxisLeftInset - _xAxisRightInset);
|
|
|
|
return (
|
|
|
|
minX: -_xAxisLeftInset * scale,
|
|
|
|
maxX: count - 1 + _xAxisRightInset * scale,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
int? _dayIndexForTouch(double? dx, double width) {
|
|
|
|
final count = widget.days.length;
|
|
|
|
if (dx == null || width <= 0 || count <= 0) return null;
|
|
|
|
if (count == 1 || width <= _xAxisLeftInset + _xAxisRightInset) {
|
|
|
|
return dx <= width - _xAxisRightInset ? 0 : null;
|
|
|
|
}
|
|
|
|
final dataWidth = width - _xAxisLeftInset - _xAxisRightInset;
|
|
|
|
final firstX = _xAxisLeftInset;
|
|
|
|
final lastX = width - _xAxisRightInset;
|
|
|
|
final step = dataWidth / (count - 1);
|
|
|
|
final minTouchX = (firstX - step / 2).clamp(0, width).toDouble();
|
|
|
|
final maxTouchX = (lastX + step / 2)
|
|
|
|
.clamp(0, width - _ChartPlotFrame.rightAxisWidth)
|
|
|
|
.toDouble();
|
|
|
|
if (dx < minTouchX || dx > maxTouchX) return null;
|
|
|
|
return ((dx - _xAxisLeftInset) / dataWidth * (count - 1))
|
|
|
|
.round()
|
|
|
|
.clamp(0, count - 1)
|
|
|
|
.toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
void _scheduleTooltipDismissal() {
|
|
|
|
if (_selectedDayIndex == null || widget.tooltipAutoDismissMs <= 0) return;
|
|
|
|
_tooltipTimer?.cancel();
|
|
...
|
...
|
@@ -1588,7 +1611,9 @@ class _ChartPlotFrame extends StatelessWidget { |
|
|
|
padding: EdgeInsets.only(
|
|
|
|
top: topInset,
|
|
|
|
left: insetChildHorizontally ? horizontalInset : 0,
|
|
|
|
right: insetChildHorizontally ? rightAxisWidth : 0,
|
|
|
|
right: insetChildHorizontally
|
|
|
|
? rightAxisWidth
|
|
|
|
: 0,
|
|
|
|
),
|
|
|
|
child: child,
|
|
|
|
),
|
...
|
...
|
|