|
...
|
...
|
@@ -6,7 +6,6 @@ import 'package:flutter/material.dart'; |
|
|
|
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
|
|
|
|
import '../../../../r.dart';
|
|
|
|
import '../../report_common/widgets/health_report_subject_scope.dart';
|
|
|
|
import '../models/activity_burn_report_models.dart';
|
|
|
|
import 'activity_burn_trend_lines.dart';
|
|
...
|
...
|
@@ -21,7 +20,6 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
|
|
|
|
static const _h1 = Color(0xFF0F0F11);
|
|
|
|
static const _h3 = Color(0xFFB0B0B6);
|
|
|
|
static const _brand = Color(0xFF845EEE);
|
|
|
|
static const _active = Color(0xFFFF5279);
|
|
|
|
static const _warm = Color(0xFFFF9A6E);
|
|
|
|
static const _stand = Color(0xFF7B9BFB);
|
|
...
|
...
|
@@ -41,8 +39,6 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final start = _startTime;
|
|
|
|
final axis = _HeartRateAxis.fromSummary(summary, start);
|
|
|
|
final sleepRange = summary.hasData ? _sleepRange(start, axis.maxX) : null;
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
height: 273,
|
|
|
|
padding: const EdgeInsets.fromLTRB(20, 20, 14, 16),
|
|
...
|
...
|
@@ -69,7 +65,7 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
SizedBox(
|
|
|
|
height: _chartHeight,
|
|
|
|
child: LayoutBuilder(
|
|
|
|
builder: (context, constraints) {
|
|
|
|
builder: (_, __) {
|
|
|
|
return Stack(
|
|
|
|
fit: StackFit.expand,
|
|
|
|
alignment: Alignment.center,
|
|
...
|
...
|
@@ -89,20 +85,6 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
top: 0,
|
|
|
|
child: LineChart(_chartData()),
|
|
|
|
),
|
|
|
|
if (sleepRange != null)
|
|
|
|
Positioned(
|
|
|
|
top: 0,
|
|
|
|
left: _sleepIconLeft(
|
|
|
|
sleepRange,
|
|
|
|
axis.maxX,
|
|
|
|
constraints.maxWidth,
|
|
|
|
),
|
|
|
|
child: Image.asset(
|
|
|
|
R.assetsImagesHealthBedIcon,
|
|
|
|
width: 12,
|
|
|
|
height: 12,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
if (!summary.hasData)
|
|
|
|
Positioned(
|
|
|
|
left: 0,
|
|
...
|
...
|
@@ -130,25 +112,10 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
double _sleepIconLeft(
|
|
|
|
_SleepRange range,
|
|
|
|
double maxX,
|
|
|
|
double chartWidth,
|
|
|
|
) {
|
|
|
|
const rightTitleWidth = 28.0;
|
|
|
|
const iconWidth = 12.0;
|
|
|
|
final plotWidth = math.max(0, chartWidth - rightTitleWidth);
|
|
|
|
final centerX = (range.startX + range.endX) / 2;
|
|
|
|
return (centerX / maxX * plotWidth - iconWidth / 2)
|
|
|
|
.clamp(0, math.max(0, chartWidth - iconWidth))
|
|
|
|
.toDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
LineChartData _chartData() {
|
|
|
|
final start = _startTime;
|
|
|
|
final axis = _HeartRateAxis.fromSummary(summary, start);
|
|
|
|
final points = _points(start, axis.endTime);
|
|
|
|
final sleepRange = _sleepRange(start, axis.maxX);
|
|
|
|
|
|
|
|
return LineChartData(
|
|
|
|
minX: 0,
|
|
...
|
...
|
@@ -219,11 +186,7 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
),
|
|
|
|
lineBarsData: points.isEmpty
|
|
|
|
? [_emptyPlaceholderBar(axis)]
|
|
|
|
: [
|
|
|
|
if (sleepRange != null)
|
|
|
|
_sleepRangeBar(sleepRange, axis.maxY, axis.minY),
|
|
|
|
..._lineSegments(points, start),
|
|
|
|
],
|
|
|
|
: _lineSegments(points, start),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
...
|
...
|
@@ -240,47 +203,6 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
LineChartBarData _sleepRangeBar(
|
|
|
|
_SleepRange range,
|
|
|
|
double maxY,
|
|
|
|
double minY,
|
|
|
|
) {
|
|
|
|
return LineChartBarData(
|
|
|
|
spots: [FlSpot(range.startX, maxY), FlSpot(range.endX, maxY)],
|
|
|
|
color: _brand,
|
|
|
|
barWidth: 2,
|
|
|
|
isCurved: false,
|
|
|
|
dotData: const FlDotData(show: false),
|
|
|
|
belowBarData: BarAreaData(
|
|
|
|
show: true,
|
|
|
|
cutOffY: minY,
|
|
|
|
applyCutOffY: true,
|
|
|
|
gradient: const LinearGradient(
|
|
|
|
begin: Alignment.topCenter,
|
|
|
|
end: Alignment.bottomCenter,
|
|
|
|
colors: [Color(0x4D845EEE), Color(0x00845EEE)],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
_SleepRange? _sleepRange(DateTime start, double maxX) {
|
|
|
|
final sleepStart = summary.sleepStartTime;
|
|
|
|
final sleepEnd = summary.sleepEndTime;
|
|
|
|
if (sleepStart == null ||
|
|
|
|
sleepEnd == null ||
|
|
|
|
!sleepEnd.isAfter(sleepStart)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
final startX = sleepStart.difference(start).inMinutes.toDouble();
|
|
|
|
final endX = sleepEnd.difference(start).inMinutes.toDouble();
|
|
|
|
final visibleStart = startX.clamp(0, maxX).toDouble();
|
|
|
|
final visibleEnd = endX.clamp(0, maxX).toDouble();
|
|
|
|
if (visibleEnd <= visibleStart) return null;
|
|
|
|
return _SleepRange(visibleStart, visibleEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
DateTime get _startTime {
|
|
|
|
if (summary.startTime != null) return _dayStart(summary.startTime!);
|
|
|
|
if (summary.points.isNotEmpty) return _dayStart(summary.points.first.time);
|
|
...
|
...
|
@@ -531,10 +453,3 @@ class _HeartRateSegment { |
|
|
|
final Color color;
|
|
|
|
final List<FlSpot> spots;
|
|
|
|
} |
|
|
|
|
|
|
|
class _SleepRange {
|
|
|
|
const _SleepRange(this.startX, this.endX);
|
|
|
|
|
|
|
|
final double startX;
|
|
|
|
final double endX;
|
|
|
|
} |
...
|
...
|
|