Commit 335cf3f5c04ae84e973bba0f17fdf476e94db479

Authored by 权海
1 parent 41f680fe

feat(ui):修复睡眠平均评分、themepreview页面错误

... ... @@ -39,7 +39,6 @@ class CustomWatchThemePreviewView
return WatchThemeHeader(
title: controller.themeItem.title,
themeImageUrl: controller.themeItem.energeticImage,
errorDesc: null,
);
}),
const SizedBox(height: 26),
... ...
... ... @@ -26,7 +26,6 @@ class WatchThemePreviewView extends GetView<WatchThemePreviewController> {
Obx(() {
return WatchThemeHeader(
themeImageUrl: controller.themeItem.energeticImage,
errorDesc: null,
);
}),
const SizedBox(height: 26),
... ...
import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
import 'package:flutter/material.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:get/get.dart';
import 'watch_face_preview.dart';
import 'watch_theme_colors.dart';
... ... @@ -10,19 +8,15 @@ class WatchThemeHeader extends StatelessWidget {
const WatchThemeHeader({
super.key,
this.title,
this.errorDesc,
this.themeImageUrl,
});
final String? title;
final String? errorDesc;
final String? themeImageUrl;
@override
Widget build(BuildContext context) {
final environmentConfig = Get.find<AppEnvironmentConfig>();
return Column(
children: [
SizedBox(height: 12),
... ... @@ -42,18 +36,6 @@ class WatchThemeHeader extends StatelessWidget {
height: 1.25,
),
),
if (environmentConfig.isDebug && errorDesc != null) ...[
SizedBox(height: 8),
Text(
errorDesc!,
style: TextStyle(
color: WatchThemeColors.pro,
fontSize: 14,
fontWeight: FontWeight.w400,
),
maxLines: 2,
),
],
],
);
}
... ...
... ... @@ -90,8 +90,8 @@ class LocalHealthDataConvert {
avgSleepScore: _averageOrNull(
validSleep.map((e) => e.score).whereType<num>().toList(),
),
avgSleepEvaluate: _averageOrNull(
validSleep.map((e) => e.state?.value).whereType<num>().toList(),
avgSleepEvaluate: _sleepEvaluateFromAverageScore(
validSleep.map((e) => e.score).whereType<num>().toList(),
),
qoqAvgSleepDuration: _averageOrNull(
previousValidSleep.map((e) => e.durationSeconds).toList(),
... ... @@ -99,8 +99,8 @@ class LocalHealthDataConvert {
qoqSleepScore: _averageOrNull(
previousValidSleep.map((e) => e.score).whereType<num>().toList(),
),
qoqSleepEvaluate: _averageOrNull(
previousValidSleep.map((e) => e.state?.value).whereType<num>().toList(),
qoqSleepEvaluate: _sleepEvaluateFromAverageScore(
previousValidSleep.map((e) => e.score).whereType<num>().toList(),
),
sleepTrendList: [
for (final item in daily)
... ... @@ -176,8 +176,8 @@ class LocalHealthDataConvert {
avgSleepScore: _averageOrNull(
validSleep.map((e) => e.sleepScore).toList(),
),
avgSleepEvaluate: _averageOrNull(
validSleep.map((e) => e.sleepStateValue).whereType<num>().toList(),
avgSleepEvaluate: _sleepEvaluateFromAverageScore(
validSleep.map((e) => e.sleepScore).toList(),
),
qoqAvgSleepDuration: _averageOrNull(
previousValidSleep.map((e) => e.sleepMinutes * 60).toList(),
... ... @@ -185,11 +185,8 @@ class LocalHealthDataConvert {
qoqSleepScore: _averageOrNull(
previousValidSleep.map((e) => e.sleepScore).toList(),
),
qoqSleepEvaluate: _averageOrNull(
previousValidSleep
.map((e) => e.sleepStateValue)
.whereType<num>()
.toList(),
qoqSleepEvaluate: _sleepEvaluateFromAverageScore(
previousValidSleep.map((e) => e.sleepScore).toList(),
),
sleepTrendList: [
for (var index = 0; index < days.length; index++)
... ... @@ -993,6 +990,12 @@ class LocalHealthDataConvert {
return _sum(values) / values.length;
}
static int? _sleepEvaluateFromAverageScore(List<num> scores) {
final averageScore = _averageOrNull(scores);
if (averageScore == null) return null;
return HealthSleepCalculator.sleepState(averageScore.round())?.value;
}
static num? _maxOrNull(List<num> values) {
if (values.isEmpty) return null;
return values.reduce(math.max);
... ...
import 'package:doublefeel_flutter/core/services/health_sleep_calculator.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('HealthSleepCalculator sleep state', () {
test('uses 60 and 85 as score boundaries', () {
expect(HealthSleepCalculator.sleepState(0), isNull);
expect(HealthSleepCalculator.sleepState(59), HealthSleepState.poor);
expect(HealthSleepCalculator.sleepState(60), HealthSleepState.good);
expect(HealthSleepCalculator.sleepState(84), HealthSleepState.good);
expect(HealthSleepCalculator.sleepState(85), HealthSleepState.great);
});
});
}
... ...
... ... @@ -82,6 +82,43 @@ void main() {
expect(statistics.avgSleepDuration, isNull);
});
test('maps average sleep score to average sleep evaluate', () {
final firstDay = DateTime(2026, 7, 14);
final secondDay = DateTime(2026, 7, 15);
final thirdDay = DateTime(2026, 7, 16);
HealthRawSleepResult result(DateTime day, int score, int state) {
final endTime = LocalHealthDataConvert.unixSeconds(
day.add(const Duration(hours: 7)),
);
return HealthRawSleepResult(
userId: 1,
date: endTime,
startDate: endTime - const Duration(hours: 7).inSeconds,
sleepScore: score,
sleepState: state,
inBedMinutes: 430,
awakMinutes: 20,
sleepMinutes: 400,
);
}
final statistics = LocalHealthDataConvert.sleepStatisticsFromResults(
dateRangeType: 0,
days: [firstDay, secondDay, thirdDay],
previousDays: const [],
sleepResults: [
result(firstDay, 50, 3),
result(secondDay, 50, 3),
result(thirdDay, 90, 1),
],
heartRate: const [],
);
expect(statistics.avgSleepScore, closeTo(63.333, 0.001));
expect(statistics.avgSleepEvaluate, 2);
});
test('sleep duration excludes awake intervals inside sleep range', () {
final day = DateTime(2026, 7, 16);
final sleepStart = DateTime(2026, 7, 15, 23, 56);
... ...