Commit 9b3061e1d15ddf34d0646984127cca9aec720b68

Authored by 常守达
1 parent 1c42efc7

feat(today): 首页UI

1 import 'dart:async'; 1 import 'dart:async';
2 2
  3 +import 'package:doublefeel_flutter/app/modules/home/views/no_health_data_page.dart';
3 import 'package:doublefeel_flutter/core/logging/app_logger.dart'; 4 import 'package:doublefeel_flutter/core/logging/app_logger.dart';
4 import 'package:doublefeel_flutter/core/network/api/health_api.dart'; 5 import 'package:doublefeel_flutter/core/network/api/health_api.dart';
5 import 'package:doublefeel_flutter/core/network/api/user_api.dart'; 6 import 'package:doublefeel_flutter/core/network/api/user_api.dart';
@@ -101,12 +102,13 @@ class TodayController extends GetxController { @@ -101,12 +102,13 @@ class TodayController extends GetxController {
101 final hrvAnnotations = <HrvAnnotation>[].obs; 102 final hrvAnnotations = <HrvAnnotation>[].obs;
102 103
103 Future<void> requestHealthAuthorization() async { 104 Future<void> requestHealthAuthorization() async {
104 - try {  
105 - await _healthKitUploadService.requestClientAuthorization();  
106 - await _refreshHealthAuthorizationState();  
107 - } catch (error) {  
108 - debugPrint('Health authorization skipped: $error');  
109 - } 105 + // try {
  106 + // await _healthKitUploadService.requestClientAuthorization();
  107 + // await _refreshHealthAuthorizationState();
  108 + // } catch (error) {
  109 + // debugPrint('Health authorization skipped: $error');
  110 + // }
  111 + Get.to(NoHealthDataPage());
110 } 112 }
111 113
112 @override 114 @override
@@ -199,7 +201,8 @@ class TodayController extends GetxController { @@ -199,7 +201,8 @@ class TodayController extends GetxController {
199 isOther: false, 201 isOther: false,
200 errorHandlingPolicy: null, 202 errorHandlingPolicy: null,
201 ) 203 )
202 - : Future.value(AppFailure<TodayStatusData>(AppUnknownError('Not today'))); 204 + : Future.value(
  205 + AppFailure<TodayStatusData>(AppUnknownError('Not today')));
203 206
204 final latestHrvResultFuture = isToday 207 final latestHrvResultFuture = isToday
205 ? _healthApi.getLatestHrvData( 208 ? _healthApi.getLatestHrvData(
@@ -395,7 +398,8 @@ class TodayController extends GetxController { @@ -395,7 +398,8 @@ class TodayController extends GetxController {
395 } 398 }
396 } 399 }
397 400
398 - void _applyActivityStatistics(ActivityBurnStatisticsData data, DateTime date) { 401 + void _applyActivityStatistics(
  402 + ActivityBurnStatisticsData data, DateTime date) {
399 _activityMoveTarget = data.activityTargetInfo?.move ?? _activityMoveTarget; 403 _activityMoveTarget = data.activityTargetInfo?.move ?? _activityMoveTarget;
400 _activityStandTarget = 404 _activityStandTarget =
401 data.activityTargetInfo?.stand ?? _activityStandTarget; 405 data.activityTargetInfo?.stand ?? _activityStandTarget;
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:flutter/material.dart';
  3 +import 'package:flutter/services.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +class NoHealthDataPage extends StatelessWidget {
  7 + const NoHealthDataPage({
  8 + super.key,
  9 + this.onRefresh,
  10 + this.onHelp,
  11 + });
  12 +
  13 + static const _backgroundColor = Color(0xFFF5F2FF);
  14 +
  15 + static const _placeholderColor = Color(0xFFD9D9D9);
  16 + static const _imagePlaceholderColor = Color(0xFFF3F3F3);
  17 + static const _screenShotBackground = Color(0xFFF2F2F6);
  18 +
  19 + final VoidCallback? onRefresh;
  20 + final VoidCallback? onHelp;
  21 +
  22 + @override
  23 + Widget build(BuildContext context) {
  24 + return AnnotatedRegion<SystemUiOverlayStyle>(
  25 + value: AppTheme.systemUiOverlayStyle.copyWith(
  26 + systemNavigationBarColor: _backgroundColor,
  27 + ),
  28 + child: Scaffold(
  29 + backgroundColor: _backgroundColor,
  30 + body: Column(
  31 + children: [
  32 + SafeArea(
  33 + bottom: false,
  34 + child: _TopBar(onRefresh: onRefresh),
  35 + ),
  36 + Expanded(
  37 + child: ListView(
  38 + physics: const ClampingScrollPhysics(),
  39 + padding: EdgeInsets.fromLTRB(
  40 + 16,
  41 + 4,
  42 + 16,
  43 + 20 + MediaQuery.paddingOf(context).bottom,
  44 + ),
  45 + children: [
  46 + const _PageHeading(),
  47 + const SizedBox(height: 24),
  48 + const _InfoCard(
  49 + title: '错误1:Apple Watch 数据不可用',
  50 + body:
  51 + '看起来你在过去 12 个月内没有使用 Apple Watch。如果你刚开始使用 Apple Watch 并已启用所有数据权限,这条信息可能仍会出现。请继续佩戴 Apple Watch 以允许数据采集,或在首页手动添加 HRV 数据。',
  52 + ),
  53 + const SizedBox(height: 12),
  54 + const _AuthorizationCard(),
  55 + const SizedBox(height: 12),
  56 + const _SystemIssueCard(),
  57 + const SizedBox(height: 16),
  58 + Center(
  59 + child: GestureDetector(
  60 + behavior: HitTestBehavior.opaque,
  61 + onTap: onHelp,
  62 + child: Padding(
  63 + padding: const EdgeInsets.symmetric(
  64 + horizontal: 16,
  65 + vertical: 4,
  66 + ),
  67 + child: Text(
  68 + '需要帮助?',
  69 + style: TextStyle(
  70 + color: context.colors.primary,
  71 + fontSize: 12,
  72 + fontWeight: FontWeight.w400,
  73 + height: 17 / 12,
  74 + ),
  75 + ),
  76 + ),
  77 + ),
  78 + ),
  79 + ],
  80 + ),
  81 + ),
  82 + ],
  83 + ),
  84 + ),
  85 + );
  86 + }
  87 +}
  88 +
  89 +class _TopBar extends StatelessWidget {
  90 + const _TopBar({this.onRefresh});
  91 +
  92 + final VoidCallback? onRefresh;
  93 +
  94 + @override
  95 + Widget build(BuildContext context) {
  96 + return SizedBox(
  97 + height: 44,
  98 + child: Stack(
  99 + children: [
  100 + Positioned(
  101 + left: 4,
  102 + top: 0,
  103 + bottom: 0,
  104 + child: GestureDetector(
  105 + behavior: HitTestBehavior.opaque,
  106 + onTap: () => Get.back<void>(),
  107 + child: const SizedBox(
  108 + width: 44,
  109 + height: 44,
  110 + child: Center(
  111 + child: Image(
  112 + image: AssetImage('assets/images/common/ic_nav_back.webp'),
  113 + width: 24,
  114 + height: 24,
  115 + ),
  116 + ),
  117 + ),
  118 + ),
  119 + ),
  120 + Positioned(
  121 + right: 16,
  122 + top: 0,
  123 + bottom: 0,
  124 + child: GestureDetector(
  125 + behavior: HitTestBehavior.opaque,
  126 + onTap: onRefresh,
  127 + child: Center(
  128 + child: Text(
  129 + '刷新',
  130 + style: TextStyle(
  131 + color: context.colors.primary,
  132 + fontSize: 14,
  133 + fontWeight: FontWeight.w400,
  134 + height: 20 / 14,
  135 + ),
  136 + ),
  137 + ),
  138 + ),
  139 + ),
  140 + ],
  141 + ),
  142 + );
  143 + }
  144 +}
  145 +
  146 +class _PageHeading extends StatelessWidget {
  147 + const _PageHeading();
  148 +
  149 + @override
  150 + Widget build(BuildContext context) {
  151 + return Column(
  152 + children: [
  153 + Padding(
  154 + padding: EdgeInsets.symmetric(horizontal: 21),
  155 + child: Text(
  156 + '无心率数据可用',
  157 + textAlign: TextAlign.center,
  158 + style: TextStyle(
  159 + color: Color(0xFF14121E),
  160 + fontSize: 20,
  161 + fontWeight: FontWeight.w600,
  162 + height: 28 / 20,
  163 + ),
  164 + ),
  165 + ),
  166 + SizedBox(height: 8),
  167 + Padding(
  168 + padding: EdgeInsets.symmetric(horizontal: 8),
  169 + child: Text(
  170 + 'DoubleFeel无法从 Apple Health 获取你的 HRV 数据。请按照提示授予权限,然后点击右上角“刷新”继续。',
  171 + textAlign: TextAlign.center,
  172 + style: TextStyle(
  173 + color: context.colors.textPrimary,
  174 + fontSize: 14,
  175 + fontWeight: FontWeight.w400,
  176 + height: 18 / 14,
  177 + ),
  178 + ),
  179 + ),
  180 + ],
  181 + );
  182 + }
  183 +}
  184 +
  185 +class _InfoCard extends StatelessWidget {
  186 + const _InfoCard({
  187 + required this.title,
  188 + required this.body,
  189 + });
  190 +
  191 + final String title;
  192 + final String body;
  193 +
  194 + @override
  195 + Widget build(BuildContext context) {
  196 + return _CardShell(
  197 + child: Column(
  198 + crossAxisAlignment: CrossAxisAlignment.start,
  199 + children: [
  200 + const Center(child: _IconPlaceholder()),
  201 + const SizedBox(height: 10),
  202 + Text(
  203 + title,
  204 + style: TextStyle(
  205 + color: Colors.black,
  206 + fontSize: 16,
  207 + fontWeight: FontWeight.w600,
  208 + height: 22 / 16,
  209 + ),
  210 + ),
  211 + const SizedBox(height: 6),
  212 + Text(
  213 + body,
  214 + style: TextStyle(
  215 + color: context.colors.textSecondary,
  216 + fontSize: 12,
  217 + fontWeight: FontWeight.w400,
  218 + height: 17 / 12,
  219 + ),
  220 + ),
  221 + ],
  222 + ),
  223 + );
  224 + }
  225 +}
  226 +
  227 +class _AuthorizationCard extends StatelessWidget {
  228 + const _AuthorizationCard();
  229 +
  230 + @override
  231 + Widget build(BuildContext context) {
  232 + return _CardShell(
  233 + child: Column(
  234 + crossAxisAlignment: CrossAxisAlignment.start,
  235 + children: [
  236 + Center(child: _IconPlaceholder()),
  237 + SizedBox(height: 10),
  238 + Text(
  239 + '错误 2:健康数据访问未授权',
  240 + style: TextStyle(
  241 + color: Colors.black,
  242 + fontSize: 16,
  243 + fontWeight: FontWeight.w600,
  244 + height: 22 / 16,
  245 + ),
  246 + ),
  247 + SizedBox(height: 6),
  248 + Text(
  249 + 'DoubleFeel 需要访问 Apple Health 数据的权限,以提供压力统计、提醒和建议。如果未授权,部分功能可能无法正常使用。\n\n请放心,所有健康数据仅存储在本地,不会上传。\n\n要启用权限,请按提示操作,并在 iOS 设置中选择 允许所有 → 健康 → DoubleFeel。',
  250 + style: TextStyle(
  251 + color: context.colors.textSecondary,
  252 + fontSize: 12,
  253 + fontWeight: FontWeight.w400,
  254 + height: 17 / 12,
  255 + ),
  256 + ),
  257 + SizedBox(height: 37),
  258 + Image.asset(
  259 + 'assets/images/today/health_data_permission_step_1.png',
  260 + height: 140,
  261 + alignment: Alignment(0, 0.12),
  262 + ),
  263 + SizedBox(height: 20),
  264 + Image.asset(
  265 + 'assets/images/today/health_data_permission_step_2.png',
  266 + height: 172,
  267 + alignment: Alignment(0, 0.24),
  268 + ),
  269 + SizedBox(height: 20),
  270 + Image.asset(
  271 + 'assets/images/today/health_data_permission_step_3.png',
  272 + height: 210,
  273 + alignment: Alignment(0, -0.13),
  274 + ),
  275 + SizedBox(height: 20),
  276 + Image.asset(
  277 + 'assets/images/today/health_data_permission_step_4.png',
  278 + height: 167,
  279 + alignment: Alignment(0, -0.54),
  280 + ),
  281 + ],
  282 + ),
  283 + );
  284 + }
  285 +}
  286 +
  287 +class _SystemIssueCard extends StatelessWidget {
  288 + const _SystemIssueCard();
  289 +
  290 + @override
  291 + Widget build(BuildContext context) {
  292 + return _CardShell(
  293 + child: Column(
  294 + crossAxisAlignment: CrossAxisAlignment.start,
  295 + children: [
  296 + Center(child: _IconPlaceholder()),
  297 + SizedBox(height: 10),
  298 + Text(
  299 + '错误 3:系统问题',
  300 + style: TextStyle(
  301 + color: Colors.black,
  302 + fontSize: 16,
  303 + fontWeight: FontWeight.w600,
  304 + height: 22 / 16,
  305 + ),
  306 + ),
  307 + SizedBox(height: 6),
  308 + Text(
  309 + '根据用户反馈,我们发现 HRV 或心率数据可能缺失的两个原因:\n\n'
  310 + '1. Apple Watch 未连接\n'
  311 + ' · 如果你的 Apple Watch 长时间未佩戴,心率数据可能无法采集。\n'
  312 + ' · 请检查 iOS 健康 App → “我的手表”,确认最近的心率数据是否在佩戴 Apple Watch 时记录。\n'
  313 + ' · 如果没有,请尝试佩戴 Apple Watch 进行数据采集,并打开 Apple 支持的心率功能。\n\n'
  314 + '2. 过去 30 天的心率或 HRV 数据缺失\n'
  315 + ' · 打开 iOS 健康 App → 浏览 → “心率” 或 “HRV” → “未找到数据”,确认是否缺失。\n'
  316 + ' · 如果数据缺失,请重新佩戴手表,并重启 iPhone 和 Apple Watch 后,再次打开 DoubleFeel。',
  317 + style: TextStyle(
  318 + color: context.colors.textSecondary,
  319 + fontSize: 12,
  320 + fontWeight: FontWeight.w400,
  321 + height: 17 / 12,
  322 + ),
  323 + ),
  324 + ],
  325 + ),
  326 + );
  327 + }
  328 +}
  329 +
  330 +class _CardShell extends StatelessWidget {
  331 + const _CardShell({required this.child});
  332 +
  333 + final Widget child;
  334 +
  335 + @override
  336 + Widget build(BuildContext context) {
  337 + return Container(
  338 + width: double.infinity,
  339 + padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
  340 + decoration: BoxDecoration(
  341 + color: Colors.white,
  342 + borderRadius: BorderRadius.circular(16),
  343 + ),
  344 + child: child,
  345 + );
  346 + }
  347 +}
  348 +
  349 +class _IconPlaceholder extends StatelessWidget {
  350 + const _IconPlaceholder();
  351 +
  352 + @override
  353 + Widget build(BuildContext context) {
  354 + return Container(
  355 + width: 56,
  356 + height: 56,
  357 + color: NoHealthDataPage._placeholderColor,
  358 + );
  359 + }
  360 +}
1 import 'package:doublefeel_flutter/core/services/user_state_service.dart'; 1 import 'package:doublefeel_flutter/core/services/user_state_service.dart';
2 import 'package:doublefeel_flutter/core/theme/app_theme.dart'; 2 import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  3 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
3 import 'package:flutter/material.dart'; 4 import 'package:flutter/material.dart';
4 import 'package:get/get.dart'; 5 import 'package:get/get.dart';
5 import 'package:table_calendar/table_calendar.dart'; 6 import 'package:table_calendar/table_calendar.dart';
6 7
7 import '../../controllers/today_controller.dart'; 8 import '../../controllers/today_controller.dart';
  9 +import '../../widgets/today/hrv_measurement_bottom_sheet.dart';
8 import '../../widgets/today/today_health_data_auth_card.dart'; 10 import '../../widgets/today/today_health_data_auth_card.dart';
9 import '../../widgets/today/today_hrv_ad_banner.dart'; 11 import '../../widgets/today/today_hrv_ad_banner.dart';
10 -import '../../widgets/today/today_hrv_number_card.dart';  
11 import '../../widgets/today/today_hrv_chart_card.dart'; 12 import '../../widgets/today/today_hrv_chart_card.dart';
  13 +import '../../widgets/today/today_hrv_number_card.dart';
12 import '../../widgets/today/today_sleep_activity_cards.dart'; 14 import '../../widgets/today/today_sleep_activity_cards.dart';
  15 +import '../../widgets/today/stress_status_explanation_bottom_sheet.dart';
13 16
14 import '../../widgets/today/premium_card.dart'; 17 import '../../widgets/today/premium_card.dart';
15 18
@@ -110,13 +113,29 @@ class TodayTab extends GetView<TodayController> { @@ -110,13 +113,29 @@ class TodayTab extends GetView<TodayController> {
110 mainAxisAlignment: MainAxisAlignment.end, 113 mainAxisAlignment: MainAxisAlignment.end,
111 children: [ 114 children: [
112 Obx( 115 Obx(
113 - () => Text(  
114 - controller.stressSubtitle.value,  
115 - textAlign: TextAlign.center,  
116 - style: const TextStyle(  
117 - color: Color(0xFF78787D),  
118 - fontSize: 14,  
119 - fontWeight: FontWeight.w500, 116 + () => GestureDetector(
  117 + behavior: HitTestBehavior.opaque,
  118 + onTap: showStressStatusExplanationBottomSheet,
  119 + child: Row(
  120 + mainAxisAlignment: MainAxisAlignment.center,
  121 + children: [
  122 + Text(
  123 + controller.stressSubtitle.value,
  124 + textAlign: TextAlign.center,
  125 + style: const TextStyle(
  126 + color: Color(0xFF78787D),
  127 + fontSize: 14,
  128 + fontWeight: FontWeight.w500,
  129 + ),
  130 + ),
  131 + const SizedBox(width: 4),
  132 + Image.asset(
  133 + 'assets/images/common/ic_info.png',
  134 + width: 14,
  135 + height: 14,
  136 + color: const Color(0xFF78787D),
  137 + ),
  138 + ],
120 ), 139 ),
121 ), 140 ),
122 ), 141 ),
@@ -124,8 +143,8 @@ class TodayTab extends GetView<TodayController> { @@ -124,8 +143,8 @@ class TodayTab extends GetView<TodayController> {
124 () => Text( 143 () => Text(
125 controller.stressLabel.value, 144 controller.stressLabel.value,
126 textAlign: TextAlign.center, 145 textAlign: TextAlign.center,
127 - style: const TextStyle(  
128 - color: Color(0xFF0F0F11), 146 + style: TextStyle(
  147 + color: context.colors.textPrimary,
129 fontSize: 28, 148 fontSize: 28,
130 fontWeight: FontWeight.w500, 149 fontWeight: FontWeight.w500,
131 ), 150 ),
@@ -161,21 +180,7 @@ class TodayTab extends GetView<TodayController> { @@ -161,21 +180,7 @@ class TodayTab extends GetView<TodayController> {
161 const TodaySleepCard(), 180 const TodaySleepCard(),
162 const TodayActivityCard(), 181 const TodayActivityCard(),
163 const SizedBox(height: 4), 182 const SizedBox(height: 4),
164 - Container(  
165 - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),  
166 - decoration: BoxDecoration(  
167 - color: Colors.white,  
168 - borderRadius: BorderRadius.circular(16),  
169 - ),  
170 - child: const Text(  
171 - '立即测量HRV',  
172 - style: TextStyle(  
173 - color: Color(0xFF0F0F11),  
174 - fontSize: 16,  
175 - fontWeight: FontWeight.w600,  
176 - ),  
177 - ),  
178 - ), 183 + measureHRVButton(context),
179 Container( 184 Container(
180 alignment: Alignment.center, 185 alignment: Alignment.center,
181 margin: EdgeInsets.only( 186 margin: EdgeInsets.only(
@@ -195,6 +200,27 @@ class TodayTab extends GetView<TodayController> { @@ -195,6 +200,27 @@ class TodayTab extends GetView<TodayController> {
195 ); 200 );
196 } 201 }
197 202
  203 + Widget measureHRVButton(BuildContext context) {
  204 + return GestureDetector(
  205 + onTap: showHrvMeasurementBottomSheet,
  206 + child: Container(
  207 + padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
  208 + decoration: BoxDecoration(
  209 + color: Colors.white,
  210 + borderRadius: BorderRadius.circular(16),
  211 + ),
  212 + child: Text(
  213 + context.l10n.measureYourHrvNow,
  214 + style: TextStyle(
  215 + color: context.colors.textPrimary,
  216 + fontSize: 16,
  217 + fontWeight: FontWeight.w600,
  218 + ),
  219 + ),
  220 + ),
  221 + );
  222 + }
  223 +
198 Widget _buildWeekCalendar(BuildContext context) { 224 Widget _buildWeekCalendar(BuildContext context) {
199 return SizedBox( 225 return SizedBox(
200 height: _weekCalendarHeight, 226 height: _weekCalendarHeight,
@@ -224,7 +250,6 @@ class TodayTab extends GetView<TodayController> { @@ -224,7 +250,6 @@ class TodayTab extends GetView<TodayController> {
224 selectedDayPredicate: (day) => 250 selectedDayPredicate: (day) =>
225 isSameDay(controller.selectedDate.value, day), 251 isSameDay(controller.selectedDate.value, day),
226 onDaySelected: _onDaySelected, 252 onDaySelected: _onDaySelected,
227 - onPageChanged: _onCalendarPageChanged,  
228 calendarStyle: const CalendarStyle( 253 calendarStyle: const CalendarStyle(
229 cellMargin: EdgeInsets.zero, 254 cellMargin: EdgeInsets.zero,
230 cellPadding: EdgeInsets.zero, 255 cellPadding: EdgeInsets.zero,
@@ -235,7 +260,8 @@ class TodayTab extends GetView<TodayController> { @@ -235,7 +260,8 @@ class TodayTab extends GetView<TodayController> {
235 decoration: BoxDecoration(), 260 decoration: BoxDecoration(),
236 ), 261 ),
237 calendarBuilders: CalendarBuilders( 262 calendarBuilders: CalendarBuilders(
238 - dowBuilder: (context, day) => _buildWeekdayCell(context, day), 263 + dowBuilder: (context, day) =>
  264 + _buildWeekdayCell(context, day),
239 defaultBuilder: (context, day, focusedDay) => 265 defaultBuilder: (context, day, focusedDay) =>
240 _buildDateCell(context, day), 266 _buildDateCell(context, day),
241 disabledBuilder: (context, day, focusedDay) => 267 disabledBuilder: (context, day, focusedDay) =>
@@ -288,7 +314,9 @@ class TodayTab extends GetView<TodayController> { @@ -288,7 +314,9 @@ class TodayTab extends GetView<TodayController> {
288 ) 314 )
289 : null, 315 : null,
290 child: Text( 316 child: Text(
291 - isSameDay(day, controller.lastSelectableDay) ? '今' : _weekdayText(day), 317 + isSameDay(day, controller.lastSelectableDay)
  318 + ? '今'
  319 + : _weekdayText(day),
292 style: TextStyle( 320 style: TextStyle(
293 color: _calendarTextColor(context, day, selected: selected), 321 color: _calendarTextColor(context, day, selected: selected),
294 fontSize: 12, 322 fontSize: 12,
@@ -333,23 +361,10 @@ class TodayTab extends GetView<TodayController> { @@ -333,23 +361,10 @@ class TodayTab extends GetView<TodayController> {
333 controller.changeDate(selectedDay, focused: focusedDay); 361 controller.changeDate(selectedDay, focused: focusedDay);
334 } 362 }
335 363
336 - void _onCalendarPageChanged(DateTime focusedDay) {  
337 - final selectedWeekdayOffset =  
338 - controller.selectedDate.value.weekday - DateTime.monday;  
339 - final nextSelectedDay =  
340 - _weekStart(focusedDay).add(Duration(days: selectedWeekdayOffset));  
341 - controller.changeDate(nextSelectedDay, focused: focusedDay);  
342 - }  
343 -  
344 void _backToToday() { 364 void _backToToday() {
345 controller.changeDate(controller.lastSelectableDay); 365 controller.changeDate(controller.lastSelectableDay);
346 } 366 }
347 367
348 - DateTime _weekStart(DateTime day) {  
349 - final normalizedDay = DateUtils.dateOnly(day);  
350 - return normalizedDay.subtract(Duration(days: normalizedDay.weekday - 1));  
351 - }  
352 -  
353 String _dateTitle(DateTime selectedDay) { 368 String _dateTitle(DateTime selectedDay) {
354 final today = controller.lastSelectableDay; 369 final today = controller.lastSelectableDay;
355 if (isSameDay(selectedDay, today)) return '今日'; 370 if (isSameDay(selectedDay, today)) return '今日';
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +void showHrvMeasurementBottomSheet() {
  7 + Get.bottomSheet(
  8 + const FractionallySizedBox(
  9 + heightFactor: 0.9,
  10 + child: HrvMeasurementBottomSheet(),
  11 + ),
  12 + barrierColor: Colors.black.withValues(alpha: 0.7),
  13 + enableDrag: true,
  14 + isScrollControlled: true,
  15 + persistent: false,
  16 + );
  17 +}
  18 +
  19 +class HrvMeasurementBottomSheet extends StatelessWidget {
  20 + const HrvMeasurementBottomSheet({super.key});
  21 +
  22 + @override
  23 + Widget build(BuildContext context) {
  24 + final l10n = context.l10n;
  25 +
  26 + return Container(
  27 + decoration: const BoxDecoration(
  28 + color: Color(0xFFF5F2FF),
  29 + borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  30 + ),
  31 + child: SafeArea(
  32 + top: false,
  33 + child: Column(
  34 + children: [
  35 + SizedBox(
  36 + height: 64,
  37 + child: Stack(
  38 + alignment: Alignment.center,
  39 + children: [
  40 + Positioned(
  41 + left: 16,
  42 + child: GestureDetector(
  43 + behavior: HitTestBehavior.opaque,
  44 + onTap: Get.back,
  45 + child: SizedBox(
  46 + width: 44,
  47 + height: 44,
  48 + child: Center(
  49 + child: Image.asset(
  50 + 'assets/images/common/ic_close.png',
  51 + width: 20,
  52 + height: 20,
  53 + color: context.colors.chartPurple,
  54 + ),
  55 + ),
  56 + ),
  57 + ),
  58 + ),
  59 + ],
  60 + ),
  61 + ),
  62 + Expanded(
  63 + child: Padding(
  64 + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
  65 + child: Center(
  66 + child: ConstrainedBox(
  67 + constraints: const BoxConstraints(maxWidth: 342),
  68 + child: const _HrvInstructionCard(),
  69 + ),
  70 + ),
  71 + ),
  72 + ),
  73 + Container(
  74 + height: 48,
  75 + width: 280,
  76 + margin: const EdgeInsets.only(bottom: 20),
  77 + child: ElevatedButton(
  78 + onPressed: Get.back,
  79 + style: ElevatedButton.styleFrom(
  80 + backgroundColor: context.colors.primary,
  81 + foregroundColor: Colors.white,
  82 + elevation: 0,
  83 + shape: RoundedRectangleBorder(
  84 + borderRadius: BorderRadius.circular(24),
  85 + ),
  86 + ),
  87 + child: Text(
  88 + l10n.todayBottomSheetGotIt,
  89 + style: TextStyle(
  90 + fontSize: 16,
  91 + fontWeight: FontWeight.w600,
  92 + ),
  93 + ),
  94 + ),
  95 + ),
  96 + ],
  97 + ),
  98 + ),
  99 + );
  100 + }
  101 +}
  102 +
  103 +class _HrvInstructionCard extends StatelessWidget {
  104 + const _HrvInstructionCard();
  105 +
  106 + @override
  107 + Widget build(BuildContext context) {
  108 + final l10n = context.l10n;
  109 +
  110 + return Stack(
  111 + alignment: Alignment.topCenter,
  112 + children: [
  113 + Container(
  114 + constraints: const BoxConstraints(minHeight: 438),
  115 + margin: const EdgeInsets.only(top: 110),
  116 + padding: const EdgeInsets.fromLTRB(20, 78, 20, 20),
  117 + decoration: BoxDecoration(
  118 + color: Colors.white,
  119 + borderRadius: BorderRadius.circular(16),
  120 + ),
  121 + child: Column(
  122 + crossAxisAlignment: CrossAxisAlignment.start,
  123 + children: [
  124 + Text(
  125 + l10n.todayHrvMeasurementIntro,
  126 + style: TextStyle(
  127 + color: context.colors.textSecondary,
  128 + fontSize: 12,
  129 + height: 17 / 12,
  130 + fontWeight: FontWeight.w400,
  131 + ),
  132 + ),
  133 + const SizedBox(height: 18),
  134 + _InstructionStep(text: l10n.todayHrvMeasurementStep1),
  135 + const SizedBox(height: 8),
  136 + _InstructionStep(text: l10n.todayHrvMeasurementStep2),
  137 + const SizedBox(height: 8),
  138 + _InstructionStep(text: l10n.todayHrvMeasurementStep3),
  139 + const SizedBox(height: 8),
  140 + _InstructionStep(text: l10n.todayHrvMeasurementStep4),
  141 + const SizedBox(height: 8),
  142 + _InstructionStep(text: l10n.todayHrvMeasurementStep5),
  143 + const SizedBox(height: 16),
  144 + Text(
  145 + l10n.todayHrvMeasurementHint,
  146 + style: TextStyle(
  147 + color: context.colors.textSecondary,
  148 + fontSize: 12,
  149 + height: 17 / 12,
  150 + fontWeight: FontWeight.w400,
  151 + ),
  152 + ),
  153 + const SizedBox(height: 8),
  154 + Text(
  155 + l10n.todayHrvMeasurementWarning,
  156 + style: TextStyle(
  157 + color: context.colors.warning,
  158 + fontSize: 12,
  159 + height: 17 / 12,
  160 + fontWeight: FontWeight.w400,
  161 + ),
  162 + ),
  163 + ],
  164 + ),
  165 + ),
  166 + Image.asset(
  167 + 'assets/images/today/hrv_measure_watch.png',
  168 + width: 110,
  169 + height: 172,
  170 + fit: BoxFit.contain,
  171 + ),
  172 + ],
  173 + );
  174 + }
  175 +}
  176 +
  177 +class _InstructionStep extends StatelessWidget {
  178 + const _InstructionStep({required this.text});
  179 +
  180 + final String text;
  181 +
  182 + @override
  183 + Widget build(BuildContext context) {
  184 + return Text(
  185 + text,
  186 + style: TextStyle(
  187 + color: context.colors.textPrimary,
  188 + fontSize: 14,
  189 + height: 20 / 14,
  190 + fontWeight: FontWeight.w400,
  191 + ),
  192 + );
  193 + }
  194 +}
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +import 'today_faq_bottom_sheet.dart';
  7 +
  8 +void showHrvPrincipleExplanationBottomSheet() {
  9 + Get.bottomSheet(
  10 + DraggableScrollableSheet(
  11 + initialChildSize: 0.96,
  12 + minChildSize: 0.2,
  13 + maxChildSize: 0.96,
  14 + expand: false,
  15 + snap: true,
  16 + builder: (context, scrollController) {
  17 + return HrvPrincipleExplanationBottomSheet(
  18 + scrollController: scrollController,
  19 + );
  20 + },
  21 + ),
  22 + barrierColor: Colors.black.withValues(alpha: 0.7),
  23 + enableDrag: true,
  24 + isScrollControlled: true,
  25 + persistent: false,
  26 + );
  27 +}
  28 +
  29 +class HrvPrincipleExplanationBottomSheet extends StatelessWidget {
  30 + const HrvPrincipleExplanationBottomSheet({
  31 + super.key,
  32 + required this.scrollController,
  33 + });
  34 +
  35 + final ScrollController scrollController;
  36 +
  37 + @override
  38 + Widget build(BuildContext context) {
  39 + final l10n = context.l10n;
  40 +
  41 + return Container(
  42 + decoration: const BoxDecoration(
  43 + color: Color(0xFFF5F2FF),
  44 + borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  45 + ),
  46 + child: SafeArea(
  47 + top: false,
  48 + child: Column(
  49 + children: [
  50 + SizedBox(
  51 + height: 64,
  52 + child: Stack(
  53 + alignment: Alignment.center,
  54 + children: [
  55 + Center(
  56 + child: Text(
  57 + l10n.todayHrvPrincipleTitle,
  58 + style: TextStyle(
  59 + color: context.colors.textPrimary,
  60 + fontSize: 16,
  61 + fontWeight: FontWeight.w600,
  62 + ),
  63 + )),
  64 + Positioned(
  65 + left: 16,
  66 + child: GestureDetector(
  67 + behavior: HitTestBehavior.opaque,
  68 + onTap: Get.back,
  69 + child: SizedBox(
  70 + width: 44,
  71 + height: 44,
  72 + child: Center(
  73 + child: Image.asset(
  74 + 'assets/images/common/ic_close.png',
  75 + width: 20,
  76 + height: 20,
  77 + color: context.colors.chartPurple,
  78 + ),
  79 + ),
  80 + ),
  81 + ),
  82 + ),
  83 + ],
  84 + ),
  85 + ),
  86 + Expanded(
  87 + child: ListView(
  88 + controller: scrollController,
  89 + physics: const ClampingScrollPhysics(),
  90 + padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
  91 + children: [
  92 + _InfoCard(
  93 + title: l10n.todayHrvPrincipleHowMeasureTitle,
  94 + children: [
  95 + _InfoText(
  96 + l10n.todayHrvPrincipleHowMeasureDescription1,
  97 + ),
  98 + _InfoText(
  99 + l10n.todayHrvPrincipleHowMeasureDescription2,
  100 + ),
  101 + _InfoText(
  102 + l10n.todayHrvPrincipleHowMeasureDescription3,
  103 + ),
  104 + _InfoText(
  105 + l10n.todayHrvPrincipleHowMeasureDescription4,
  106 + ),
  107 + const SizedBox(height: 16),
  108 + _StatusItem(
  109 + color: const Color(0xFFFF5279),
  110 + title: l10n.todayStressStatusOverload,
  111 + body: l10n.todayStressStatusOverloadDescription,
  112 + ),
  113 + _StatusItem(
  114 + color: const Color(0xFFFF9A6E),
  115 + title: l10n.todayStressStatusCaution,
  116 + body: l10n.todayStressStatusCautionDescription,
  117 + ),
  118 + _StatusItem(
  119 + color: const Color(0xFF7B9BFB),
  120 + title: l10n.todayStressStatusNormal,
  121 + body: l10n.todayStressStatusNormalDescription,
  122 + ),
  123 + _StatusItem(
  124 + color: const Color(0xFF3BD49D),
  125 + title: l10n.todayStressStatusExcellent,
  126 + body: l10n.todayStressStatusExcellentDescription,
  127 + ),
  128 + _StatusItem(
  129 + color: const Color(0xFFD9D9D9),
  130 + title: l10n.todayStressStatusInsufficientData,
  131 + body: l10n.todayStressStatusInsufficientDataDescription,
  132 + bottomSpacing: 0,
  133 + ),
  134 + ],
  135 + ),
  136 + const SizedBox(height: 12),
  137 + const _FaqLinksCard(),
  138 + ],
  139 + ),
  140 + ),
  141 + ],
  142 + ),
  143 + ),
  144 + );
  145 + }
  146 +}
  147 +
  148 +class _InfoCard extends StatelessWidget {
  149 + const _InfoCard({required this.title, required this.children});
  150 +
  151 + final String title;
  152 + final List<Widget> children;
  153 +
  154 + @override
  155 + Widget build(BuildContext context) {
  156 + return Container(
  157 + width: double.infinity,
  158 + padding: const EdgeInsets.all(16),
  159 + decoration: BoxDecoration(
  160 + color: Colors.white,
  161 + borderRadius: BorderRadius.circular(16),
  162 + ),
  163 + child: Column(
  164 + crossAxisAlignment: CrossAxisAlignment.start,
  165 + children: [
  166 + Text(
  167 + title,
  168 + style: TextStyle(
  169 + color: context.colors.textPrimary,
  170 + fontSize: 12,
  171 + fontWeight: FontWeight.w600,
  172 + ),
  173 + ),
  174 + const SizedBox(height: 12),
  175 + ...children,
  176 + ],
  177 + ),
  178 + );
  179 + }
  180 +}
  181 +
  182 +class _InfoText extends StatelessWidget {
  183 + const _InfoText(this.text);
  184 +
  185 + final String text;
  186 +
  187 + @override
  188 + Widget build(BuildContext context) {
  189 + return Padding(
  190 + padding: const EdgeInsets.only(bottom: 3),
  191 + child: Text(
  192 + text,
  193 + style: TextStyle(
  194 + color: context.colors.textSecondary,
  195 + fontSize: 12,
  196 + height: 17 / 12,
  197 + fontWeight: FontWeight.w400,
  198 + ),
  199 + ),
  200 + );
  201 + }
  202 +}
  203 +
  204 +class _StatusItem extends StatelessWidget {
  205 + const _StatusItem({
  206 + required this.color,
  207 + required this.title,
  208 + required this.body,
  209 + this.bottomSpacing = 12,
  210 + });
  211 +
  212 + final Color color;
  213 + final String title;
  214 + final String body;
  215 + final double bottomSpacing;
  216 +
  217 + @override
  218 + Widget build(BuildContext context) {
  219 + return Padding(
  220 + padding: EdgeInsets.only(bottom: bottomSpacing),
  221 + child: Column(
  222 + crossAxisAlignment: CrossAxisAlignment.start,
  223 + children: [
  224 + Row(
  225 + children: [
  226 + Container(
  227 + width: 12,
  228 + height: 12,
  229 + decoration: BoxDecoration(color: color, shape: BoxShape.circle),
  230 + ),
  231 + const SizedBox(width: 5),
  232 + Text(
  233 + title,
  234 + style: TextStyle(
  235 + color: context.colors.textSecondary,
  236 + fontSize: 12,
  237 + height: 17 / 12,
  238 + fontWeight: FontWeight.w400,
  239 + ),
  240 + ),
  241 + ],
  242 + ),
  243 + const SizedBox(height: 2),
  244 + _InfoText(body),
  245 + ],
  246 + ),
  247 + );
  248 + }
  249 +}
  250 +
  251 +class _FaqLinksCard extends StatelessWidget {
  252 + const _FaqLinksCard();
  253 +
  254 + @override
  255 + Widget build(BuildContext context) {
  256 + final l10n = context.l10n;
  257 +
  258 + return _InfoCard(
  259 + title: l10n.todayFaqSectionTitle,
  260 + children: [
  261 + _FaqLink(l10n.todayFaqLinkNoData),
  262 + _FaqLink(l10n.todayFaqLinkHrvRealtimeUpdate),
  263 + _FaqLink(l10n.todayFaqLinkWatchNoStatusAndInteractionNotification),
  264 + _FaqLink(l10n.todayFaqLinkWatchFaceDataDelay),
  265 + _FaqLink(l10n.todayFaqLinkWatchFaceBlackScreen),
  266 + ],
  267 + );
  268 + }
  269 +}
  270 +
  271 +class _FaqLink extends StatelessWidget {
  272 + const _FaqLink(this.text);
  273 +
  274 + final String text;
  275 +
  276 + @override
  277 + Widget build(BuildContext context) {
  278 + return GestureDetector(
  279 + behavior: HitTestBehavior.opaque,
  280 + onTap: showTodayFaqBottomSheet,
  281 + child: Padding(
  282 + padding: const EdgeInsets.only(bottom: 14),
  283 + child: Text(
  284 + text,
  285 + style: TextStyle(
  286 + color: context.colors.textSecondary,
  287 + fontSize: 12,
  288 + height: 17 / 12,
  289 + fontWeight: FontWeight.w400,
  290 + decoration: TextDecoration.underline,
  291 + ),
  292 + ),
  293 + ),
  294 + );
  295 + }
  296 +}
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +import 'today_faq_bottom_sheet.dart';
  7 +
  8 +void showRealtimeStressExplanationBottomSheet() {
  9 + Get.bottomSheet(
  10 + DraggableScrollableSheet(
  11 + initialChildSize: 0.96,
  12 + minChildSize: 0.2,
  13 + maxChildSize: 0.96,
  14 + expand: false,
  15 + snap: true,
  16 + builder: (context, scrollController) {
  17 + return RealtimeStressExplanationBottomSheet(
  18 + scrollController: scrollController,
  19 + );
  20 + },
  21 + ),
  22 + barrierColor: Colors.black.withValues(alpha: 0.7),
  23 + enableDrag: true,
  24 + isScrollControlled: true,
  25 + persistent: false,
  26 + );
  27 +}
  28 +
  29 +class RealtimeStressExplanationBottomSheet extends StatelessWidget {
  30 + const RealtimeStressExplanationBottomSheet({
  31 + super.key,
  32 + required this.scrollController,
  33 + });
  34 +
  35 + final ScrollController scrollController;
  36 +
  37 + @override
  38 + Widget build(BuildContext context) {
  39 + final l10n = context.l10n;
  40 +
  41 + return Container(
  42 + decoration: const BoxDecoration(
  43 + color: Color(0xFFF5F2FF),
  44 + borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  45 + ),
  46 + child: SafeArea(
  47 + top: false,
  48 + child: Column(
  49 + children: [
  50 + SizedBox(
  51 + height: 64,
  52 + child: Stack(
  53 + alignment: Alignment.center,
  54 + children: [
  55 + Center(
  56 + child: Text(
  57 + l10n.todayRealtimeStressTitle,
  58 + style: TextStyle(
  59 + color: context.colors.textPrimary,
  60 + fontSize: 16,
  61 + fontWeight: FontWeight.w600,
  62 + ),
  63 + ),
  64 + ),
  65 + Positioned(
  66 + left: 16,
  67 + child: GestureDetector(
  68 + behavior: HitTestBehavior.opaque,
  69 + onTap: Get.back,
  70 + child: SizedBox(
  71 + width: 44,
  72 + height: 44,
  73 + child: Center(
  74 + child: Image.asset(
  75 + 'assets/images/common/ic_close.png',
  76 + width: 20,
  77 + height: 20,
  78 + color: context.colors.chartPurple,
  79 + ),
  80 + ),
  81 + ),
  82 + ),
  83 + ),
  84 + ],
  85 + ),
  86 + ),
  87 + Expanded(
  88 + child: ListView(
  89 + controller: scrollController,
  90 + physics: const ClampingScrollPhysics(),
  91 + padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
  92 + children: [
  93 + _InfoCard(
  94 + title: l10n.todayRealtimeStressWhatTitle,
  95 + children: [
  96 + _InfoText(
  97 + l10n.todayRealtimeStressWhatDescription1,
  98 + ),
  99 + _InfoText(l10n.todayRealtimeStressWhatDescription2),
  100 + _InfoText(l10n.todayRealtimeStressWhatDescription3),
  101 + ],
  102 + ),
  103 + const SizedBox(height: 12),
  104 + _InfoCard(
  105 + title: l10n.todayRealtimeStressDivisionTitle,
  106 + children: [
  107 + _InfoText(l10n.todayRealtimeStressDivisionIntro),
  108 + const SizedBox(height: 16),
  109 + _StatusItem(
  110 + color: const Color(0xFF3BD49D),
  111 + title: l10n.todayRealtimeStressExcellentRange,
  112 + body: l10n.todayRealtimeStressExcellentDescription,
  113 + ),
  114 + _StatusItem(
  115 + color: const Color(0xFF7B9BFB),
  116 + title: l10n.todayRealtimeStressNormalRange,
  117 + body: l10n.todayRealtimeStressNormalDescription,
  118 + ),
  119 + _StatusItem(
  120 + color: const Color(0xFFFF9A6E),
  121 + title: l10n.todayRealtimeStressCautionRange,
  122 + body: l10n.todayRealtimeStressCautionDescription,
  123 + ),
  124 + _StatusItem(
  125 + color: const Color(0xFFFF5279),
  126 + title: l10n.todayRealtimeStressOverloadRange,
  127 + body: l10n.todayRealtimeStressOverloadDescription,
  128 + ),
  129 + _InfoText(l10n.todayRealtimeStressDivisionBaseline),
  130 + _InfoText(l10n.todayRealtimeStressDivisionAwake),
  131 + ],
  132 + ),
  133 + const SizedBox(height: 12),
  134 + _InfoCard(
  135 + title: l10n.todayRealtimeStressLowBetterTitle,
  136 + children: [
  137 + _InfoText(l10n.todayRealtimeStressLowBetterNo),
  138 + _InfoText(l10n.todayRealtimeStressLowBetterType),
  139 + _InfoText(
  140 + l10n.todayRealtimeStressLowBetterExample,
  141 + ),
  142 + _InfoText(
  143 + l10n.todayRealtimeStressLowBetterHighStress,
  144 + ),
  145 + _InfoText(l10n.todayRealtimeStressLowBetterTrend),
  146 + ],
  147 + ),
  148 + const SizedBox(height: 12),
  149 + _InfoCard(
  150 + title: l10n.todayRealtimeStressScenarioTitle,
  151 + children: [
  152 + _InfoText(
  153 + l10n.todayRealtimeStressScenarioHrvDefault,
  154 + ),
  155 + _InfoText(
  156 + l10n.todayRealtimeStressScenarioRegionLimit,
  157 + ),
  158 + _InfoText(l10n.todayRealtimeStressScenarioIntro),
  159 + _InfoText(
  160 + l10n.todayRealtimeStressScenarioUpdateEvery6Min),
  161 + _InfoText(l10n.todayRealtimeStressScenarioTimely),
  162 + _InfoText(
  163 + l10n.todayRealtimeStressScenarioConsistentTrend),
  164 + _InfoText(l10n.todayRealtimeStressScenarioSummary),
  165 + ],
  166 + ),
  167 + const SizedBox(height: 12),
  168 + const _FaqLinksCard(),
  169 + ],
  170 + ),
  171 + ),
  172 + ],
  173 + ),
  174 + ),
  175 + );
  176 + }
  177 +}
  178 +
  179 +class _InfoCard extends StatelessWidget {
  180 + const _InfoCard({
  181 + required this.title,
  182 + required this.children,
  183 + });
  184 +
  185 + final String title;
  186 + final List<Widget> children;
  187 +
  188 + @override
  189 + Widget build(BuildContext context) {
  190 + return Container(
  191 + width: double.infinity,
  192 + padding: const EdgeInsets.all(16),
  193 + decoration: BoxDecoration(
  194 + color: Colors.white,
  195 + borderRadius: BorderRadius.circular(16),
  196 + ),
  197 + child: Column(
  198 + crossAxisAlignment: CrossAxisAlignment.start,
  199 + children: [
  200 + Text(
  201 + title,
  202 + style: TextStyle(
  203 + color: context.colors.textPrimary,
  204 + fontSize: 12,
  205 + fontWeight: FontWeight.w600,
  206 + ),
  207 + ),
  208 + const SizedBox(height: 12),
  209 + ...children,
  210 + ],
  211 + ),
  212 + );
  213 + }
  214 +}
  215 +
  216 +class _InfoText extends StatelessWidget {
  217 + const _InfoText(this.text);
  218 +
  219 + final String text;
  220 +
  221 + @override
  222 + Widget build(BuildContext context) {
  223 + return Padding(
  224 + padding: const EdgeInsets.only(bottom: 3),
  225 + child: Text(
  226 + text,
  227 + style: TextStyle(
  228 + color: context.colors.textSecondary,
  229 + fontSize: 12,
  230 + height: 17 / 12,
  231 + fontWeight: FontWeight.w400,
  232 + ),
  233 + ),
  234 + );
  235 + }
  236 +}
  237 +
  238 +class _StatusItem extends StatelessWidget {
  239 + const _StatusItem({
  240 + required this.color,
  241 + required this.title,
  242 + required this.body,
  243 + });
  244 +
  245 + final Color color;
  246 + final String title;
  247 + final String body;
  248 +
  249 + @override
  250 + Widget build(BuildContext context) {
  251 + return Padding(
  252 + padding: const EdgeInsets.only(bottom: 12),
  253 + child: Column(
  254 + crossAxisAlignment: CrossAxisAlignment.start,
  255 + children: [
  256 + Row(
  257 + children: [
  258 + Container(
  259 + width: 12,
  260 + height: 12,
  261 + decoration: BoxDecoration(color: color, shape: BoxShape.circle),
  262 + ),
  263 + const SizedBox(width: 5),
  264 + Text(
  265 + title,
  266 + style: TextStyle(
  267 + color: context.colors.textSecondary,
  268 + fontSize: 12,
  269 + height: 17 / 12,
  270 + fontWeight: FontWeight.w400,
  271 + ),
  272 + ),
  273 + ],
  274 + ),
  275 + const SizedBox(height: 2),
  276 + _InfoText(body),
  277 + ],
  278 + ),
  279 + );
  280 + }
  281 +}
  282 +
  283 +class _FaqLinksCard extends StatelessWidget {
  284 + const _FaqLinksCard();
  285 +
  286 + @override
  287 + Widget build(BuildContext context) {
  288 + final l10n = context.l10n;
  289 +
  290 + return _InfoCard(
  291 + title: l10n.todayFaqSectionTitle,
  292 + children: [
  293 + _FaqLink(l10n.todayFaqLinkNoData),
  294 + _FaqLink(l10n.todayFaqLinkHrvRealtimeUpdate),
  295 + _FaqLink(l10n.todayFaqLinkWatchNoStatusNotification),
  296 + _FaqLink(l10n.todayFaqLinkWatchFaceDataDelay),
  297 + _FaqLink(l10n.todayFaqLinkWatchFaceBlackScreen),
  298 + ],
  299 + );
  300 + }
  301 +}
  302 +
  303 +class _FaqLink extends StatelessWidget {
  304 + const _FaqLink(this.text);
  305 +
  306 + final String text;
  307 +
  308 + @override
  309 + Widget build(BuildContext context) {
  310 + return GestureDetector(
  311 + behavior: HitTestBehavior.opaque,
  312 + onTap: showTodayFaqBottomSheet,
  313 + child: Padding(
  314 + padding: const EdgeInsets.only(bottom: 14),
  315 + child: Text(
  316 + text,
  317 + style: TextStyle(
  318 + color: context.colors.textSecondary,
  319 + fontSize: 12,
  320 + height: 17 / 12,
  321 + fontWeight: FontWeight.w400,
  322 + decoration: TextDecoration.underline,
  323 + ),
  324 + ),
  325 + ),
  326 + );
  327 + }
  328 +}
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +import 'today_faq_bottom_sheet.dart';
  7 +
  8 +void showStressStatusExplanationBottomSheet() {
  9 + Get.bottomSheet(
  10 + DraggableScrollableSheet(
  11 + initialChildSize: 0.96,
  12 + minChildSize: 0.2,
  13 + maxChildSize: 0.96,
  14 + expand: false,
  15 + snap: true,
  16 + builder: (context, scrollController) {
  17 + return StressStatusExplanationBottomSheet(
  18 + scrollController: scrollController,
  19 + );
  20 + },
  21 + ),
  22 + barrierColor: Colors.black.withValues(alpha: 0.7),
  23 + enableDrag: true,
  24 + isScrollControlled: true,
  25 + persistent: false,
  26 + );
  27 +}
  28 +
  29 +class StressStatusExplanationBottomSheet extends StatelessWidget {
  30 + const StressStatusExplanationBottomSheet({
  31 + super.key,
  32 + required this.scrollController,
  33 + });
  34 +
  35 + final ScrollController scrollController;
  36 +
  37 + @override
  38 + Widget build(BuildContext context) {
  39 + final l10n = context.l10n;
  40 +
  41 + return Container(
  42 + decoration: const BoxDecoration(
  43 + color: Color(0xFFF5F2FF),
  44 + borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  45 + ),
  46 + child: SafeArea(
  47 + top: false,
  48 + child: Column(
  49 + children: [
  50 + SizedBox(
  51 + height: 64,
  52 + child: Stack(
  53 + alignment: Alignment.center,
  54 + children: [
  55 + Center(
  56 + child: Text(
  57 + l10n.todayStressStatusTitle,
  58 + style: TextStyle(
  59 + color: context.colors.textPrimary,
  60 + fontSize: 16,
  61 + fontWeight: FontWeight.w600,
  62 + ),
  63 + ),
  64 + ),
  65 + Positioned(
  66 + left: 16,
  67 + child: GestureDetector(
  68 + behavior: HitTestBehavior.opaque,
  69 + onTap: Get.back,
  70 + child: SizedBox(
  71 + width: 44,
  72 + height: 44,
  73 + child: Center(
  74 + child: Image.asset(
  75 + 'assets/images/common/ic_close.png',
  76 + width: 20,
  77 + height: 20,
  78 + color: context.colors.chartPurple,
  79 + ),
  80 + ),
  81 + ),
  82 + ),
  83 + ),
  84 + ],
  85 + ),
  86 + ),
  87 + Expanded(
  88 + child: ListView(
  89 + controller: scrollController,
  90 + physics: const ClampingScrollPhysics(),
  91 + padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
  92 + children: [
  93 + _InfoCard(
  94 + title: l10n.todayStressStatusWhatTitle,
  95 + children: [
  96 + _InfoText(
  97 + l10n.todayStressStatusWhatDescription1,
  98 + ),
  99 + _InfoText(
  100 + l10n.todayStressStatusWhatDescription2,
  101 + ),
  102 + const SizedBox(height: 16),
  103 + _StatusItem(
  104 + color: const Color(0xFFFF5279),
  105 + title: l10n.todayStressStatusOverload,
  106 + body: l10n.todayStressStatusOverloadDescription,
  107 + ),
  108 + _StatusItem(
  109 + color: const Color(0xFFFF9A6E),
  110 + title: l10n.todayStressStatusCaution,
  111 + body: l10n.todayStressStatusCautionDescription,
  112 + ),
  113 + _StatusItem(
  114 + color: const Color(0xFF7B9BFB),
  115 + title: l10n.todayStressStatusNormal,
  116 + body: l10n.todayStressStatusNormalDescription,
  117 + ),
  118 + _StatusItem(
  119 + color: const Color(0xFF3BD49D),
  120 + title: l10n.todayStressStatusExcellent,
  121 + body: l10n.todayStressStatusExcellentDescription,
  122 + ),
  123 + _StatusItem(
  124 + color: const Color(0xFFD9D9D9),
  125 + title: l10n.todayStressStatusInsufficientData,
  126 + body: l10n.todayStressStatusInsufficientDataDescription,
  127 + bottomSpacing: 0,
  128 + ),
  129 + ],
  130 + ),
  131 + const SizedBox(height: 12),
  132 + _InfoCard(
  133 + title: l10n.todayStressStatusWhyHrvTitle,
  134 + children: [
  135 + _InfoText(l10n.todayStressStatusWhyHrvDescription),
  136 + _InfoText(l10n.todayStressStatusUsually),
  137 + _InfoText(l10n.todayStressStatusHrvHigher),
  138 + _InfoText(l10n.todayStressStatusHrvLower),
  139 + _InfoText(l10n.todayStressStatusHrvChangesFast),
  140 + ],
  141 + ),
  142 + const SizedBox(height: 12),
  143 + _InfoCard(
  144 + title: l10n.todayStressStatusAppWatchDifferenceTitle,
  145 + children: [
  146 + _InfoText(l10n.todayStressStatusAppWatchDifferenceApp),
  147 + _InfoText(l10n.todayStressStatusAppWatchDifferenceWatch),
  148 + ],
  149 + ),
  150 + const SizedBox(height: 12),
  151 + _InfoCard(
  152 + title: l10n.todayStressStatusWaitingDataTitle,
  153 + children: [
  154 + _InfoText(l10n.todayStressStatusWaitingDataDescription1),
  155 + _InfoText(l10n.todayStressStatusWaitingDataDescription2),
  156 + _InfoText(l10n.todayStressStatusWaitingDataReasonsIntro),
  157 + _InfoText(l10n.todayStressStatusWaitingDataReason1),
  158 + _InfoText(l10n.todayStressStatusWaitingDataReason2),
  159 + _InfoText(l10n.todayStressStatusWaitingDataReason3),
  160 + _InfoText(l10n.todayStressStatusWaitingDataReason4),
  161 + ],
  162 + ),
  163 + const SizedBox(height: 12),
  164 + const _FaqLinksCard(),
  165 + ],
  166 + ),
  167 + ),
  168 + ],
  169 + ),
  170 + ),
  171 + );
  172 + }
  173 +}
  174 +
  175 +class _InfoCard extends StatelessWidget {
  176 + const _InfoCard({
  177 + required this.title,
  178 + required this.children,
  179 + });
  180 +
  181 + final String title;
  182 + final List<Widget> children;
  183 +
  184 + @override
  185 + Widget build(BuildContext context) {
  186 + return Container(
  187 + width: double.infinity,
  188 + padding: const EdgeInsets.all(16),
  189 + decoration: BoxDecoration(
  190 + color: Colors.white,
  191 + borderRadius: BorderRadius.circular(16),
  192 + ),
  193 + child: Column(
  194 + crossAxisAlignment: CrossAxisAlignment.start,
  195 + children: [
  196 + Text(
  197 + title,
  198 + style: TextStyle(
  199 + color: context.colors.textPrimary,
  200 + fontSize: 12,
  201 + fontWeight: FontWeight.w600,
  202 + ),
  203 + ),
  204 + const SizedBox(height: 12),
  205 + ...children,
  206 + ],
  207 + ),
  208 + );
  209 + }
  210 +}
  211 +
  212 +class _InfoText extends StatelessWidget {
  213 + const _InfoText(this.text);
  214 +
  215 + final String text;
  216 +
  217 + @override
  218 + Widget build(BuildContext context) {
  219 + return Padding(
  220 + padding: const EdgeInsets.only(bottom: 3),
  221 + child: Text(
  222 + text,
  223 + style: TextStyle(
  224 + color: context.colors.textSecondary,
  225 + fontSize: 12,
  226 + height: 17 / 12,
  227 + fontWeight: FontWeight.w400,
  228 + ),
  229 + ),
  230 + );
  231 + }
  232 +}
  233 +
  234 +class _StatusItem extends StatelessWidget {
  235 + const _StatusItem({
  236 + required this.color,
  237 + required this.title,
  238 + required this.body,
  239 + this.bottomSpacing = 12,
  240 + });
  241 +
  242 + final Color color;
  243 + final String title;
  244 + final String body;
  245 + final double bottomSpacing;
  246 +
  247 + @override
  248 + Widget build(BuildContext context) {
  249 + return Padding(
  250 + padding: EdgeInsets.only(bottom: bottomSpacing),
  251 + child: Column(
  252 + crossAxisAlignment: CrossAxisAlignment.start,
  253 + children: [
  254 + Row(
  255 + children: [
  256 + Container(
  257 + width: 12,
  258 + height: 12,
  259 + decoration: BoxDecoration(color: color, shape: BoxShape.circle),
  260 + ),
  261 + const SizedBox(width: 5),
  262 + Text(
  263 + title,
  264 + style: TextStyle(
  265 + color: context.colors.textSecondary,
  266 + fontSize: 12,
  267 + height: 17 / 12,
  268 + fontWeight: FontWeight.w400,
  269 + ),
  270 + ),
  271 + ],
  272 + ),
  273 + const SizedBox(height: 2),
  274 + _InfoText(body),
  275 + ],
  276 + ),
  277 + );
  278 + }
  279 +}
  280 +
  281 +class _FaqLinksCard extends StatelessWidget {
  282 + const _FaqLinksCard();
  283 +
  284 + @override
  285 + Widget build(BuildContext context) {
  286 + final l10n = context.l10n;
  287 +
  288 + return _InfoCard(
  289 + title: l10n.todayFaqSectionTitle,
  290 + children: [
  291 + _FaqLink(l10n.todayFaqLinkNoData),
  292 + _FaqLink(l10n.todayFaqLinkHrvRealtimeUpdate),
  293 + _FaqLink(l10n.todayFaqLinkWatchNoStatusNotification),
  294 + _FaqLink(l10n.todayFaqLinkWatchFaceDataDelay),
  295 + _FaqLink(l10n.todayFaqLinkWatchFaceBlackScreen),
  296 + ],
  297 + );
  298 + }
  299 +}
  300 +
  301 +class _FaqLink extends StatelessWidget {
  302 + const _FaqLink(this.text);
  303 +
  304 + final String text;
  305 +
  306 + @override
  307 + Widget build(BuildContext context) {
  308 + return GestureDetector(
  309 + behavior: HitTestBehavior.opaque,
  310 + onTap: showTodayFaqBottomSheet,
  311 + child: Padding(
  312 + padding: const EdgeInsets.only(bottom: 14),
  313 + child: Text(
  314 + text,
  315 + style: TextStyle(
  316 + color: context.colors.textSecondary,
  317 + fontSize: 12,
  318 + height: 17 / 12,
  319 + fontWeight: FontWeight.w400,
  320 + decoration: TextDecoration.underline,
  321 + ),
  322 + ),
  323 + ),
  324 + );
  325 + }
  326 +}
  1 +import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  2 +import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
  3 +import 'package:flutter/material.dart';
  4 +import 'package:get/get.dart';
  5 +
  6 +void showTodayFaqBottomSheet() {
  7 + Get.bottomSheet(
  8 + DraggableScrollableSheet(
  9 + initialChildSize: 0.96,
  10 + minChildSize: 0.2,
  11 + maxChildSize: 0.96,
  12 + expand: false,
  13 + snap: true,
  14 + builder: (context, scrollController) {
  15 + return TodayFaqBottomSheet(
  16 + scrollController: scrollController,
  17 + );
  18 + },
  19 + ),
  20 + barrierColor: Colors.black.withValues(alpha: 0.7),
  21 + enableDrag: true,
  22 + isScrollControlled: true,
  23 + persistent: false,
  24 + );
  25 +}
  26 +
  27 +class TodayFaqBottomSheet extends StatelessWidget {
  28 + const TodayFaqBottomSheet({
  29 + super.key,
  30 + required this.scrollController,
  31 + });
  32 +
  33 + final ScrollController scrollController;
  34 +
  35 + @override
  36 + Widget build(BuildContext context) {
  37 + final l10n = context.l10n;
  38 +
  39 + return Container(
  40 + decoration: const BoxDecoration(
  41 + color: Color(0xFFF5F2FF),
  42 + borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  43 + ),
  44 + child: SafeArea(
  45 + top: false,
  46 + child: Column(
  47 + children: [
  48 + SizedBox(
  49 + height: 64,
  50 + child: Stack(
  51 + alignment: Alignment.center,
  52 + children: [
  53 + Center(
  54 + child: Text(
  55 + l10n.todayFaqTitle,
  56 + style: TextStyle(
  57 + color: context.colors.textPrimary,
  58 + fontSize: 16,
  59 + fontWeight: FontWeight.w600,
  60 + ),
  61 + ),
  62 + ),
  63 + Positioned(
  64 + left: 16,
  65 + child: GestureDetector(
  66 + behavior: HitTestBehavior.opaque,
  67 + onTap: Get.back,
  68 + child: SizedBox(
  69 + width: 44,
  70 + height: 44,
  71 + child: Center(
  72 + child: Image.asset(
  73 + 'assets/images/common/ic_close.png',
  74 + width: 20,
  75 + height: 20,
  76 + color: context.colors.chartPurple,
  77 + ),
  78 + ),
  79 + ),
  80 + ),
  81 + ),
  82 + ],
  83 + ),
  84 + ),
  85 + Expanded(
  86 + child: ListView(
  87 + controller: scrollController,
  88 + physics: const ClampingScrollPhysics(),
  89 + padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
  90 + children: [
  91 + _InfoCard(
  92 + title: l10n.todayFaqNoDataTitle,
  93 + children: [
  94 + _InfoText(
  95 + l10n.todayFaqNoDataDescription1,
  96 + ),
  97 + _InfoText(
  98 + l10n.todayFaqNoDataDescription2,
  99 + ),
  100 + _InfoText(l10n.todayFaqNoDataDescription3),
  101 + const SizedBox(height: 8),
  102 + const _ContactText(),
  103 + ],
  104 + ),
  105 + const SizedBox(height: 12),
  106 + _InfoCard(
  107 + title: l10n.todayFaqWatchNoNotificationTitle,
  108 + children: [
  109 + _InfoText(
  110 + l10n.todayFaqWatchNoNotificationDescription1,
  111 + ),
  112 + _InfoText(
  113 + l10n.todayFaqWatchNoNotificationDescription2,
  114 + ),
  115 + _InfoText(
  116 + l10n.todayFaqWatchNoNotificationCheckPhoneNotification,
  117 + ),
  118 + _InfoText(
  119 + l10n.todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh,
  120 + ),
  121 + _InfoText(
  122 + l10n.todayFaqWatchNoNotificationCheckWatchBackgroundRefresh,
  123 + ),
  124 + _InfoText(l10n.todayFaqWatchNoNotificationCheckModes),
  125 + _InfoText(l10n.todayFaqWatchNoNotificationReinstall),
  126 + const SizedBox(height: 8),
  127 + const _ContactText(),
  128 + ],
  129 + ),
  130 + const SizedBox(height: 12),
  131 + _InfoCard(
  132 + title: l10n.todayFaqWatchFaceDelayTitle,
  133 + children: [
  134 + _InfoText(
  135 + l10n.todayFaqWatchFaceDelayDescription1,
  136 + ),
  137 + _InfoText(l10n.todayFaqWatchFaceDelayIfOverOneHour),
  138 + _InfoText(l10n.todayFaqWatchFaceDelayOpenWatchApp),
  139 + _InfoText(l10n.todayFaqWatchFaceDelayIfStill),
  140 + _InfoText(l10n.todayFaqWatchFaceDelayRestartApp),
  141 + _InfoText(l10n.todayFaqWatchFaceDelayCheckIntro),
  142 + _InfoText(l10n.todayFaqWatchFaceDelayCheckData),
  143 + _InfoText(l10n.todayFaqWatchFaceDelayCheckPhoneHealth),
  144 + _InfoText(l10n.todayFaqWatchFaceDelayCheckWatchHealth),
  145 + _InfoText(
  146 + l10n.todayFaqWatchFaceDelayCheckBackgroundRefresh,
  147 + ),
  148 + _InfoText(l10n.todayFaqWatchFaceDelayRestartWatch),
  149 + ],
  150 + ),
  151 + const SizedBox(height: 12),
  152 + _InfoCard(
  153 + title: l10n.todayFaqWatchFaceBlackScreenTitle,
  154 + children: [
  155 + _InfoText(
  156 + l10n.todayFaqWatchFaceBlackScreenDescription,
  157 + ),
  158 + ],
  159 + ),
  160 + ],
  161 + ),
  162 + ),
  163 + ],
  164 + ),
  165 + ),
  166 + );
  167 + }
  168 +}
  169 +
  170 +class _InfoCard extends StatelessWidget {
  171 + const _InfoCard({
  172 + required this.title,
  173 + required this.children,
  174 + });
  175 +
  176 + final String title;
  177 + final List<Widget> children;
  178 +
  179 + @override
  180 + Widget build(BuildContext context) {
  181 + return Container(
  182 + width: double.infinity,
  183 + padding: const EdgeInsets.all(16),
  184 + decoration: BoxDecoration(
  185 + color: Colors.white,
  186 + borderRadius: BorderRadius.circular(16),
  187 + ),
  188 + child: Column(
  189 + crossAxisAlignment: CrossAxisAlignment.start,
  190 + children: [
  191 + Text(
  192 + title,
  193 + style: TextStyle(
  194 + color: context.colors.textPrimary,
  195 + fontSize: 12,
  196 + fontWeight: FontWeight.w600,
  197 + ),
  198 + ),
  199 + const SizedBox(height: 12),
  200 + ...children,
  201 + ],
  202 + ),
  203 + );
  204 + }
  205 +}
  206 +
  207 +class _InfoText extends StatelessWidget {
  208 + const _InfoText(this.text);
  209 +
  210 + final String text;
  211 +
  212 + @override
  213 + Widget build(BuildContext context) {
  214 + return Padding(
  215 + padding: const EdgeInsets.only(bottom: 3),
  216 + child: Text(
  217 + text,
  218 + style: TextStyle(
  219 + color: context.colors.textSecondary,
  220 + fontSize: 12,
  221 + height: 17 / 12,
  222 + fontWeight: FontWeight.w400,
  223 + ),
  224 + ),
  225 + );
  226 + }
  227 +}
  228 +
  229 +class _ContactText extends StatelessWidget {
  230 + const _ContactText();
  231 +
  232 + @override
  233 + Widget build(BuildContext context) {
  234 + final l10n = context.l10n;
  235 +
  236 + return RichText(
  237 + text: TextSpan(
  238 + style: TextStyle(
  239 + color: context.colors.textSecondary,
  240 + fontSize: 12,
  241 + height: 17 / 12,
  242 + fontWeight: FontWeight.w400,
  243 + ),
  244 + children: [
  245 + TextSpan(text: l10n.todayFaqContactPrefix),
  246 + TextSpan(
  247 + text: l10n.todayFaqContactAction,
  248 + style: TextStyle(
  249 + color: context.colors.primary,
  250 + fontWeight: FontWeight.w600,
  251 + decoration: TextDecoration.underline,
  252 + ),
  253 + ),
  254 + TextSpan(text: l10n.todayFaqContactSuffix),
  255 + ],
  256 + ),
  257 + );
  258 + }
  259 +}
@@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; @@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
4 import 'package:get/get.dart'; 4 import 'package:get/get.dart';
5 5
6 import '../../controllers/today_controller.dart'; 6 import '../../controllers/today_controller.dart';
  7 +import 'hrv_principle_explanation_bottom_sheet.dart';
  8 +import 'realtime_stress_explanation_bottom_sheet.dart';
7 9
8 // X轴:动态时间 10 // X轴:动态时间
9 // 一天以6小时为一个刻度划分 11 // 一天以6小时为一个刻度划分
@@ -56,7 +58,15 @@ class TodayHrvChartCard extends GetView<TodayController> { @@ -56,7 +58,15 @@ class TodayHrvChartCard extends GetView<TodayController> {
56 ), 58 ),
57 ), 59 ),
58 const SizedBox(width: 4), 60 const SizedBox(width: 4),
59 - const Icon(Icons.info_outline, size: 14, color: _h3), 61 + GestureDetector(
  62 + behavior: HitTestBehavior.opaque,
  63 + onTap: showRealtimeStressExplanationBottomSheet,
  64 + child: const SizedBox(
  65 + width: 26,
  66 + height: 26,
  67 + child: Icon(Icons.info_outline, size: 14, color: _h3),
  68 + ),
  69 + ),
60 const Spacer(), 70 const Spacer(),
61 ], 71 ],
62 ), 72 ),
@@ -85,11 +95,21 @@ class TodayHrvChartCard extends GetView<TodayController> { @@ -85,11 +95,21 @@ class TodayHrvChartCard extends GetView<TodayController> {
85 ), 95 ),
86 ), 96 ),
87 const SizedBox(width: 4), 97 const SizedBox(width: 4),
88 - Image.asset(  
89 - 'assets/images/common/ic_info.png',  
90 - width: 14,  
91 - height: 14,  
92 - color: context.colors.textTertiary, 98 + GestureDetector(
  99 + behavior: HitTestBehavior.opaque,
  100 + onTap: showHrvPrincipleExplanationBottomSheet,
  101 + child: SizedBox(
  102 + width: 26,
  103 + height: 26,
  104 + child: Center(
  105 + child: Image.asset(
  106 + 'assets/images/common/ic_info.png',
  107 + width: 14,
  108 + height: 14,
  109 + color: context.colors.textTertiary,
  110 + ),
  111 + ),
  112 + ),
93 ), 113 ),
94 const Spacer(), 114 const Spacer(),
95 Text( 115 Text(
@@ -42,7 +42,7 @@ class AppColors { @@ -42,7 +42,7 @@ class AppColors {
42 // ========================================== 42 // ==========================================
43 static const backgroundLight = Color(0xFFFAFAFE); 43 static const backgroundLight = Color(0xFFFAFAFE);
44 static const brandBackgroundLight = Color(0xFFEAE3FF); 44 static const brandBackgroundLight = Color(0xFFEAE3FF);
45 - 45 + static const warning = Color(0xFFFC4447);
46 // ========================================== 46 // ==========================================
47 // 渐变基础色 (Gradient Components) 47 // 渐变基础色 (Gradient Components)
48 // ========================================== 48 // ==========================================
@@ -34,6 +34,8 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -34,6 +34,8 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
34 // Gradients 34 // Gradients
35 final LinearGradient brandBackgroundGradient; 35 final LinearGradient brandBackgroundGradient;
36 36
  37 + final Color warning;
  38 +
37 const AppColorsExtension({ 39 const AppColorsExtension({
38 required this.primary, 40 required this.primary,
39 required this.textPrimary, 41 required this.textPrimary,
@@ -53,6 +55,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -53,6 +55,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
53 required this.backgroundLight, 55 required this.backgroundLight,
54 required this.brandBackgroundLight, 56 required this.brandBackgroundLight,
55 required this.brandBackgroundGradient, 57 required this.brandBackgroundGradient,
  58 + required this.warning,
56 }); 59 });
57 60
58 /// The standard light palette derived directly from Figma. 61 /// The standard light palette derived directly from Figma.
@@ -80,6 +83,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -80,6 +83,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
80 begin: Alignment.centerLeft, 83 begin: Alignment.centerLeft,
81 end: Alignment.centerRight, 84 end: Alignment.centerRight,
82 ), 85 ),
  86 + warning: Color(0xFFFC4447),
83 ); 87 );
84 } 88 }
85 89
@@ -108,6 +112,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -108,6 +112,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
108 begin: Alignment.centerLeft, 112 begin: Alignment.centerLeft,
109 end: Alignment.centerRight, 113 end: Alignment.centerRight,
110 ), 114 ),
  115 + warning: AppColors.warning,
111 ); 116 );
112 } 117 }
113 118
@@ -131,6 +136,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -131,6 +136,7 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
131 Color? backgroundLight, 136 Color? backgroundLight,
132 Color? brandBackgroundLight, 137 Color? brandBackgroundLight,
133 LinearGradient? brandBackgroundGradient, 138 LinearGradient? brandBackgroundGradient,
  139 + Color? warning,
134 }) { 140 }) {
135 return AppColorsExtension( 141 return AppColorsExtension(
136 primary: primary ?? this.primary, 142 primary: primary ?? this.primary,
@@ -150,7 +156,9 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -150,7 +156,9 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
150 vipPurple: vipPurple ?? this.vipPurple, 156 vipPurple: vipPurple ?? this.vipPurple,
151 backgroundLight: backgroundLight ?? this.backgroundLight, 157 backgroundLight: backgroundLight ?? this.backgroundLight,
152 brandBackgroundLight: brandBackgroundLight ?? this.brandBackgroundLight, 158 brandBackgroundLight: brandBackgroundLight ?? this.brandBackgroundLight,
153 - brandBackgroundGradient: brandBackgroundGradient ?? this.brandBackgroundGradient, 159 + brandBackgroundGradient:
  160 + brandBackgroundGradient ?? this.brandBackgroundGradient,
  161 + warning: warning ?? this.warning,
154 ); 162 );
155 } 163 }
156 164
@@ -176,8 +184,11 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> { @@ -176,8 +184,11 @@ class AppColorsExtension extends ThemeExtension<AppColorsExtension> {
176 vipGold: Color.lerp(vipGold, other.vipGold, t)!, 184 vipGold: Color.lerp(vipGold, other.vipGold, t)!,
177 vipPurple: Color.lerp(vipPurple, other.vipPurple, t)!, 185 vipPurple: Color.lerp(vipPurple, other.vipPurple, t)!,
178 backgroundLight: Color.lerp(backgroundLight, other.backgroundLight, t)!, 186 backgroundLight: Color.lerp(backgroundLight, other.backgroundLight, t)!,
179 - brandBackgroundLight: Color.lerp(brandBackgroundLight, other.brandBackgroundLight, t)!,  
180 - brandBackgroundGradient: LinearGradient.lerp(brandBackgroundGradient, other.brandBackgroundGradient, t)!, 187 + brandBackgroundLight:
  188 + Color.lerp(brandBackgroundLight, other.brandBackgroundLight, t)!,
  189 + brandBackgroundGradient: LinearGradient.lerp(
  190 + brandBackgroundGradient, other.brandBackgroundGradient, t)!,
  191 + warning: Color.lerp(warning, other.warning, t)!,
181 ); 192 );
182 } 193 }
183 } 194 }
@@ -85,7 +85,6 @@ @@ -85,7 +85,6 @@
85 "onboardingResearchHealthy": "Feeling Great", 85 "onboardingResearchHealthy": "Feeling Great",
86 "onboardingResearchPoorSleep": "Poor Sleep", 86 "onboardingResearchPoorSleep": "Poor Sleep",
87 "onboardingResearchGoodSleep": "Good Sleep", 87 "onboardingResearchGoodSleep": "Good Sleep",
88 -  
89 "loginSlogan": "Start your pressure alert and health companion journey\nso love and care are always present", 88 "loginSlogan": "Start your pressure alert and health companion journey\nso love and care are always present",
90 "loginWithPhone": "Sign in with Phone", 89 "loginWithPhone": "Sign in with Phone",
91 "loginWithApple": "Sign in with Apple", 90 "loginWithApple": "Sign in with Apple",
@@ -94,7 +93,6 @@ @@ -94,7 +93,6 @@
94 "loginTerms": "Terms of Service", 93 "loginTerms": "Terms of Service",
95 "loginAgreementAnd": " and ", 94 "loginAgreementAnd": " and ",
96 "loginPrivacy": "Privacy Policy", 95 "loginPrivacy": "Privacy Policy",
97 -  
98 "phoneLoginHello": "Hello", 96 "phoneLoginHello": "Hello",
99 "phoneLoginWelcome": "Welcome to Double Feel", 97 "phoneLoginWelcome": "Welcome to Double Feel",
100 "phoneLoginPhoneHint": "Enter phone number", 98 "phoneLoginPhoneHint": "Enter phone number",
@@ -105,13 +103,126 @@ @@ -105,13 +103,126 @@
105 "phoneLoginCodeHint": "Enter verification code", 103 "phoneLoginCodeHint": "Enter verification code",
106 "phoneLoginAutoRegisterHint": "Unregistered numbers will be registered automatically", 104 "phoneLoginAutoRegisterHint": "Unregistered numbers will be registered automatically",
107 "phoneLoginLoggingIn": "Signing in...", 105 "phoneLoginLoggingIn": "Signing in...",
108 -  
109 "loginAgreeToTermsToast": "Please read and agree to the Terms of Service and Privacy Policy first", 106 "loginAgreeToTermsToast": "Please read and agree to the Terms of Service and Privacy Policy first",
110 "phoneLoginInvalidPhone": "Invalid phone number", 107 "phoneLoginInvalidPhone": "Invalid phone number",
111 "phoneLoginCodeSentSuccess": "Code sent", 108 "phoneLoginCodeSentSuccess": "Code sent",
112 "phoneLoginInvalidCode": "Invalid verification code", 109 "phoneLoginInvalidCode": "Invalid verification code",
113 -  
114 "todayHealthDataAuthTitle": "Unable to access heart rate health data", 110 "todayHealthDataAuthTitle": "Unable to access heart rate health data",
115 "todayHealthDataAuthDescription": "DoubleFeel needs permission to access your health data to provide stress reminders, real-time stress statistics, and health suggestions. Otherwise, some app features may not work properly. Your health data is stored locally only and will not be uploaded to any server.", 111 "todayHealthDataAuthDescription": "DoubleFeel needs permission to access your health data to provide stress reminders, real-time stress statistics, and health suggestions. Otherwise, some app features may not work properly. Your health data is stored locally only and will not be uploaded to any server.",
116 - "todayHealthDataAuthAction": "Authorize health data access" 112 + "todayHealthDataAuthAction": "Authorize health data access",
  113 + "measureYourHrvNow": "Measure your HRV now",
  114 + "todayBottomSheetGotIt": "Got it",
  115 + "todayFaqTitle": "FAQ",
  116 + "todayFaqSectionTitle": "DoubleFeel FAQ",
  117 + "todayFaqLinkNoData": "What if the app or watch face has no data?",
  118 + "todayFaqLinkHrvRealtimeUpdate": "How can HRV data update in real time?",
  119 + "todayFaqLinkWatchNoStatusNotification": "Why can't my watch receive status notifications?",
  120 + "todayFaqLinkWatchNoStatusAndInteractionNotification": "Why can't my watch receive status and interaction notifications?",
  121 + "todayFaqLinkWatchFaceDataDelay": "Why is watch face data delayed or not updating?",
  122 + "todayFaqLinkWatchFaceBlackScreen": "Why does the watch face turn black?",
  123 + "todayStressStatusTitle": "Overall stress status",
  124 + "todayHrvPrincipleTitle": "How HRV is measured",
  125 + "todayRealtimeStressTitle": "How real-time stress works",
  126 + "todayStressStatusOverload": "Stress overload",
  127 + "todayStressStatusCaution": "Stress warning",
  128 + "todayStressStatusNormal": "Normal",
  129 + "todayStressStatusExcellent": "Excellent",
  130 + "todayStressStatusInsufficientData": "Insufficient data",
  131 + "todayStressStatusOverloadDescription": "Your current HRV is much lower than your long-term average, which may indicate fatigue, high stress, or insufficient recovery. Rest is recommended.",
  132 + "todayStressStatusCautionDescription": "Your current HRV is below the normal range, and your body may be accumulating stress. Pay attention to rest and recovery.",
  133 + "todayStressStatusNormalDescription": "Your current body state is within your normal fluctuation range.",
  134 + "todayStressStatusExcellentDescription": "Your current HRV is higher than your recent average, indicating better recovery and overall state.",
  135 + "todayStressStatusInsufficientDataDescription": "There is not enough available data to accurately assess your stress state yet.",
  136 + "todayHrvMeasurementIntro": "Apple Watch measures HRV every 2-5 hours by default. If you want to measure it manually right now, follow these steps:",
  137 + "todayHrvMeasurementStep1": "1. Wear your Apple Watch snugly, sit down, and stay calm",
  138 + "todayHrvMeasurementStep2": "2. Open Mindfulness on Apple Watch and start Breathe",
  139 + "todayHrvMeasurementStep3": "3. Keep breathing steadily and wait 1-3 minutes",
  140 + "todayHrvMeasurementStep4": "4. After breathing is complete, lock and unlock your iPhone once",
  141 + "todayHrvMeasurementStep5": "5. Wait about one minute. StressWatch will receive and display your data",
  142 + "todayHrvMeasurementHint": "Tip: Data comes from Apple Watch. After measurement, there may be delays or data may not sync immediately. If this happens, measure again and wait for the data to be read.",
  143 + "todayHrvMeasurementWarning": "Note: Health permissions must be enabled, and Low Power Mode must be turned off.",
  144 + "todayStressStatusWhatTitle": "What is overall stress status?",
  145 + "todayStressStatusWhatDescription1": "DoubleFeel combines your HRV (heart rate variability), resting heart rate, and body-state changes from the past 30 days to assess your overall stress level.",
  146 + "todayStressStatusWhatDescription2": "Because HRV fluctuates with emotions, exercise, sleep, and fatigue, a single reading has limited value. We recommend focusing on your overall stress status across the day, which is more stable and useful. It helps you understand your body state and helps close contacts notice changes in time.",
  147 + "todayStressStatusWhyHrvTitle": "Why use HRV (heart rate variability)?",
  148 + "todayStressStatusWhyHrvDescription": "HRV is an important metric for measuring body stress and recovery capacity.",
  149 + "todayStressStatusUsually": "In general:",
  150 + "todayStressStatusHrvHigher": "· Higher HRV usually means better recovery",
  151 + "todayStressStatusHrvLower": "· Lower HRV may indicate fatigue, stress, or insufficient sleep",
  152 + "todayStressStatusHrvChangesFast": "· HRV changes quickly, making it useful for short-term body-state changes.",
  153 + "todayStressStatusAppWatchDifferenceTitle": "How are stress statuses on the phone app and Apple Watch different?",
  154 + "todayStressStatusAppWatchDifferenceApp": "The phone app home page shows the day's overall stress status, combining HRV, resting heart rate, and overall trends.",
  155 + "todayStressStatusAppWatchDifferenceWatch": "Apple Watch shows the most recent real-time stress status, which is better for quickly checking your current body changes.",
  156 + "todayStressStatusWaitingDataTitle": "Why does Waiting for data appear?",
  157 + "todayStressStatusWaitingDataDescription1": "Waiting for data means the current amount of collected data is not enough to generate a reliable stress assessment.",
  158 + "todayStressStatusWaitingDataDescription2": "Please keep wearing your Apple Watch and wait for the system to collect data automatically.",
  159 + "todayStressStatusWaitingDataReasonsIntro": "Possible reasons include:",
  160 + "todayStressStatusWaitingDataReason1": "1. Not enough HRV samples",
  161 + "todayStressStatusWaitingDataReason2": "2. Missing resting heart rate data",
  162 + "todayStressStatusWaitingDataReason3": "3. Apple Watch has not been worn long enough",
  163 + "todayStressStatusWaitingDataReason4": "4. Apple Health permissions are not enabled",
  164 + "todayHrvPrincipleHowMeasureTitle": "How does DoubleFeel measure stress status?",
  165 + "todayHrvPrincipleHowMeasureDescription1": "When you wear Apple Watch normally, the system automatically collects your heart rate data and syncs it to Apple Health.",
  166 + "todayHrvPrincipleHowMeasureDescription2": "DoubleFeel calculates HRV (heart rate variability) indicators based on this data to assess your body stress and recovery state.",
  167 + "todayHrvPrincipleHowMeasureDescription3": "HRV is sensitive to stress, fatigue, sleep, emotions, and recovery, so it helps us notice body-state changes earlier.",
  168 + "todayHrvPrincipleHowMeasureDescription4": "To make results more accurate, DoubleFeel compares your current HRV state with your own 30-day average instead of comparing it directly with other people.",
  169 + "todayRealtimeStressWhatTitle": "What is real-time stress?",
  170 + "todayRealtimeStressWhatDescription1": "Real-time stress is a body stress indicator dynamically generated by DoubleFeel based on your current HRV, heart rate state, and changes in your personal history.",
  171 + "todayRealtimeStressWhatDescription2": "A higher stress value means your body state is deviating more from your usual baseline and may reflect fatigue, insufficient recovery, or high stress.",
  172 + "todayRealtimeStressWhatDescription3": "It helps you notice body changes faster and adjust rest, exercise, and daily rhythm in time.",
  173 + "todayRealtimeStressDivisionTitle": "How is real-time stress divided?",
  174 + "todayRealtimeStressDivisionIntro": "Real-time stress is shown as a percentage:",
  175 + "todayRealtimeStressExcellentRange": "Excellent: 1%-20%",
  176 + "todayRealtimeStressNormalRange": "Normal: 21%-60%",
  177 + "todayRealtimeStressCautionRange": "Stress warning: 61%-80%",
  178 + "todayRealtimeStressOverloadRange": "Stress overload: 81%-100%",
  179 + "todayRealtimeStressExcellentDescription": "Your recovery state is good and you are generally relaxed.",
  180 + "todayRealtimeStressNormalDescription": "Your body is within the normal fluctuation range.",
  181 + "todayRealtimeStressCautionDescription": "Your body may be accumulating stress and needs proper rest and recovery.",
  182 + "todayRealtimeStressOverloadDescription": "Your body stress is clearly high. Reduce load and pay attention to sleep and recovery.",
  183 + "todayRealtimeStressDivisionBaseline": "These ranges are adjusted dynamically based on your personal baseline and should not be directly compared between users.",
  184 + "todayRealtimeStressDivisionAwake": "Real-time stress mainly reflects body stress changes while awake.",
  185 + "todayRealtimeStressLowBetterTitle": "Is lower real-time stress always better?",
  186 + "todayRealtimeStressLowBetterNo": "Not necessarily.",
  187 + "todayRealtimeStressLowBetterType": "Body stress can be normal or abnormal.",
  188 + "todayRealtimeStressLowBetterExample": "For example, real-time stress rising briefly during or after exercise is a normal recovery response. It can also rise temporarily during focused work or emotional excitement, which are normal body adjustments.",
  189 + "todayRealtimeStressLowBetterHighStress": "But if stress remains high while resting, sitting for a long time, or after poor sleep, it may indicate physical fatigue, mental stress, insufficient sleep recovery, incomplete exercise recovery, too much caffeine, alcohol, stimulants, or possible discomfort.",
  190 + "todayRealtimeStressLowBetterTrend": "DoubleFeel focuses more on your long-term trend than on a single fluctuation.",
  191 + "todayRealtimeStressScenarioTitle": "When should HRV and real-time stress be used?",
  192 + "todayRealtimeStressScenarioHrvDefault": "With Apple Watch default settings, HRV updates every 2-5 hours.",
  193 + "todayRealtimeStressScenarioRegionLimit": "In some regions, Apple Watch breathing features may be limited, which can affect HRV update frequency. Turning on breathing features may also consume more battery.",
  194 + "todayRealtimeStressScenarioIntro": "To address the long interval between HRV updates, DoubleFeel designed real-time stress:",
  195 + "todayRealtimeStressScenarioUpdateEvery6Min": "· Real-time stress updates every 6 minutes",
  196 + "todayRealtimeStressScenarioTimely": "· It can reflect body-state changes more promptly",
  197 + "todayRealtimeStressScenarioConsistentTrend": "· In most cases, the real-time stress trend is consistent with the HRV trend",
  198 + "todayRealtimeStressScenarioSummary": "This lets users see long-term HRV trends while also using real-time stress as a short-term body-state reference.",
  199 + "todayFaqNoDataTitle": "What if the app or watch face has no data?",
  200 + "todayFaqNoDataDescription1": "1. Confirm that Apple Watch is on watchOS 10.0 or above and iPhone is on iOS 14 or above. You can check system versions in About.",
  201 + "todayFaqNoDataDescription2": "2. Confirm all permissions are enabled: iPhone Health > Sharing > Apps > DoubleFeel > Turn On All Permissions.",
  202 + "todayFaqNoDataDescription3": "3. Confirm the device is not in Low Power Mode, low battery, or worn too loosely, as these can affect data collection.",
  203 + "todayFaqContactPrefix": "If everything above is correct, you can ",
  204 + "todayFaqContactAction": "contact us",
  205 + "todayFaqContactSuffix": ".",
  206 + "todayFaqWatchNoNotificationTitle": "Watch cannot receive status notifications?",
  207 + "todayFaqWatchNoNotificationDescription1": "Apple Watch and iPhone notifications have priority rules: when your iPhone is unlocked and the screen is on, notifications only appear on the phone and will not appear on the watch.",
  208 + "todayFaqWatchNoNotificationDescription2": "If stress data displays and updates normally but your watch does not receive notifications, try the following:",
  209 + "todayFaqWatchNoNotificationCheckPhoneNotification": "1. Check whether iPhone notifications are enabled (Settings > DoubleFeel > Notifications).",
  210 + "todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh": "2. Check whether iPhone Background App Refresh is enabled (Settings > DoubleFeel > Background App Refresh).",
  211 + "todayFaqWatchNoNotificationCheckWatchBackgroundRefresh": "3. Check whether Apple Watch Background App Refresh is enabled (Settings > General > Background App Refresh, and make sure DoubleFeel is enabled).",
  212 + "todayFaqWatchNoNotificationCheckModes": "4. Make sure Low Power, Focus, Do Not Disturb, Theater, Sleep, and similar modes are off.",
  213 + "todayFaqWatchNoNotificationReinstall": "5. Reinstall DoubleFeel and restart Apple Watch and iPhone.",
  214 + "todayFaqWatchFaceDelayTitle": "Watch face data not updating or delayed?",
  215 + "todayFaqWatchFaceDelayDescription1": "Due to Apple system limits, all watch faces, third-party or official, may have delays from a few minutes to half an hour. Developers cannot control the refresh frequency.",
  216 + "todayFaqWatchFaceDelayIfOverOneHour": "If the phone data refreshes but the watch face still has not updated after more than 1 hour:",
  217 + "todayFaqWatchFaceDelayOpenWatchApp": "Manually open DoubleFeel on Apple Watch and wait about 1 minute.",
  218 + "todayFaqWatchFaceDelayIfStill": "If it still does not update:",
  219 + "todayFaqWatchFaceDelayRestartApp": "Close the DoubleFeel background process and restart it.",
  220 + "todayFaqWatchFaceDelayCheckIntro": "If it still does not work, check:",
  221 + "todayFaqWatchFaceDelayCheckData": "· Whether both phone and watch apps can show HRV data normally.",
  222 + "todayFaqWatchFaceDelayCheckPhoneHealth": "· Make sure all permissions are enabled on iPhone: iOS Settings > Privacy & Security > Health > DoubleFeel.",
  223 + "todayFaqWatchFaceDelayCheckWatchHealth": "· Make sure all permissions are enabled on Apple Watch: Settings > Health > Data Sources & Access > DoubleFeel.",
  224 + "todayFaqWatchFaceDelayCheckBackgroundRefresh": "· Confirm DoubleFeel is enabled in Apple Watch > Settings > General > Background App Refresh.",
  225 + "todayFaqWatchFaceDelayRestartWatch": "· If it still does not refresh automatically, restart Apple Watch. Long runtimes or high background usage may cause watch face updates to pause.",
  226 + "todayFaqWatchFaceBlackScreenTitle": "Watch face turns black?",
  227 + "todayFaqWatchFaceBlackScreenDescription": "If the custom interactive watch face turns black after being added and only shows time and date, long-press the watch face, tap Edit, swipe left to Complications, choose DoubleFeel, and add each component again as needed."
117 } 228 }
@@ -88,63 +88,224 @@ @@ -88,63 +88,224 @@
88 "onboardingResearchHealthy": "身体倍儿棒", 88 "onboardingResearchHealthy": "身体倍儿棒",
89 "onboardingResearchPoorSleep": "睡眠不足", 89 "onboardingResearchPoorSleep": "睡眠不足",
90 "onboardingResearchGoodSleep": "睡眠充足", 90 "onboardingResearchGoodSleep": "睡眠充足",
91 -  
92 "loginSlogan": "开启压力预警与健康陪伴之旅\n让爱与关心从不缺席", 91 "loginSlogan": "开启压力预警与健康陪伴之旅\n让爱与关心从不缺席",
93 - "@loginSlogan": { "description": "登录页 slogan" }, 92 + "@loginSlogan": {
  93 + "description": "登录页 slogan"
  94 + },
94 "loginWithPhone": "手机号登录/注册", 95 "loginWithPhone": "手机号登录/注册",
95 - "@loginWithPhone": { "description": "手机号登录按钮" }, 96 + "@loginWithPhone": {
  97 + "description": "手机号登录按钮"
  98 + },
96 "loginWithApple": "通过Apple登录", 99 "loginWithApple": "通过Apple登录",
97 - "@loginWithApple": { "description": "Apple登录按钮" }, 100 + "@loginWithApple": {
  101 + "description": "Apple登录按钮"
  102 + },
98 "loginLastUsed": "上次使用", 103 "loginLastUsed": "上次使用",
99 - "@loginLastUsed": { "description": "上次使用标签" }, 104 + "@loginLastUsed": {
  105 + "description": "上次使用标签"
  106 + },
100 "loginAgreementPrefix": "我已阅读并同意", 107 "loginAgreementPrefix": "我已阅读并同意",
101 - "@loginAgreementPrefix": { "description": "协议勾选前缀" }, 108 + "@loginAgreementPrefix": {
  109 + "description": "协议勾选前缀"
  110 + },
102 "loginTerms": "用户协议", 111 "loginTerms": "用户协议",
103 - "@loginTerms": { "description": "用户协议链接" }, 112 + "@loginTerms": {
  113 + "description": "用户协议链接"
  114 + },
104 "loginAgreementAnd": "与", 115 "loginAgreementAnd": "与",
105 - "@loginAgreementAnd": { "description": "协议连接词" }, 116 + "@loginAgreementAnd": {
  117 + "description": "协议连接词"
  118 + },
106 "loginPrivacy": "隐私协议", 119 "loginPrivacy": "隐私协议",
107 - "@loginPrivacy": { "description": "隐私协议链接" },  
108 - 120 + "@loginPrivacy": {
  121 + "description": "隐私协议链接"
  122 + },
109 "phoneLoginHello": "Hello~", 123 "phoneLoginHello": "Hello~",
110 - "@phoneLoginHello": { "description": "手机号登录页打招呼" }, 124 + "@phoneLoginHello": {
  125 + "description": "手机号登录页打招呼"
  126 + },
111 "phoneLoginWelcome": "欢迎来到 Double Feel", 127 "phoneLoginWelcome": "欢迎来到 Double Feel",
112 - "@phoneLoginWelcome": { "description": "手机号登录页欢迎语" }, 128 + "@phoneLoginWelcome": {
  129 + "description": "手机号登录页欢迎语"
  130 + },
113 "phoneLoginPhoneHint": "请输入手机号", 131 "phoneLoginPhoneHint": "请输入手机号",
114 - "@phoneLoginPhoneHint": { "description": "手机号输入框占位符" }, 132 + "@phoneLoginPhoneHint": {
  133 + "description": "手机号输入框占位符"
  134 + },
115 "phoneLoginSendCode": "发送验证码", 135 "phoneLoginSendCode": "发送验证码",
116 - "@phoneLoginSendCode": { "description": "发送验证码按钮(首次)" }, 136 + "@phoneLoginSendCode": {
  137 + "description": "发送验证码按钮(首次)"
  138 + },
117 "phoneLoginSending": "发送中", 139 "phoneLoginSending": "发送中",
118 - "@phoneLoginSending": { "description": "发送验证码按钮(请求中)" }, 140 + "@phoneLoginSending": {
  141 + "description": "发送验证码按钮(请求中)"
  142 + },
119 "phoneLoginSentCountdown": "已发送 {countdown}s", 143 "phoneLoginSentCountdown": "已发送 {countdown}s",
120 "@phoneLoginSentCountdown": { 144 "@phoneLoginSentCountdown": {
121 "description": "倒计时文案", 145 "description": "倒计时文案",
122 "placeholders": { 146 "placeholders": {
123 - "countdown": { "type": "int" } 147 + "countdown": {
  148 + "type": "int"
  149 + }
124 } 150 }
125 }, 151 },
126 "phoneLoginResend": "重新发送", 152 "phoneLoginResend": "重新发送",
127 - "@phoneLoginResend": { "description": "倒计时结束后重新发送按钮" }, 153 + "@phoneLoginResend": {
  154 + "description": "倒计时结束后重新发送按钮"
  155 + },
128 "phoneLoginCodeHint": "请输入验证码", 156 "phoneLoginCodeHint": "请输入验证码",
129 - "@phoneLoginCodeHint": { "description": "验证码输入框占位符" }, 157 + "@phoneLoginCodeHint": {
  158 + "description": "验证码输入框占位符"
  159 + },
130 "phoneLoginAutoRegisterHint": "未注册的手机号验证通过后将自动注册", 160 "phoneLoginAutoRegisterHint": "未注册的手机号验证通过后将自动注册",
131 - "@phoneLoginAutoRegisterHint": { "description": "自动注册提示" }, 161 + "@phoneLoginAutoRegisterHint": {
  162 + "description": "自动注册提示"
  163 + },
132 "phoneLoginLoggingIn": "登录中...", 164 "phoneLoginLoggingIn": "登录中...",
133 - "@phoneLoginLoggingIn": { "description": "登录按钮 loading 文案" },  
134 - 165 + "@phoneLoginLoggingIn": {
  166 + "description": "登录按钮 loading 文案"
  167 + },
135 "loginAgreeToTermsToast": "请先阅读并同意《用户协议》与《隐私协议》", 168 "loginAgreeToTermsToast": "请先阅读并同意《用户协议》与《隐私协议》",
136 - "@loginAgreeToTermsToast": { "description": "未勾选协议时的 toast" }, 169 + "@loginAgreeToTermsToast": {
  170 + "description": "未勾选协议时的 toast"
  171 + },
137 "phoneLoginInvalidPhone": "手机号格式错误", 172 "phoneLoginInvalidPhone": "手机号格式错误",
138 - "@phoneLoginInvalidPhone": { "description": "手机号格式错误 toast" }, 173 + "@phoneLoginInvalidPhone": {
  174 + "description": "手机号格式错误 toast"
  175 + },
139 "phoneLoginCodeSentSuccess": "发送成功", 176 "phoneLoginCodeSentSuccess": "发送成功",
140 - "@phoneLoginCodeSentSuccess": { "description": "验证码发送成功 toast" }, 177 + "@phoneLoginCodeSentSuccess": {
  178 + "description": "验证码发送成功 toast"
  179 + },
141 "phoneLoginInvalidCode": "验证码格式错误", 180 "phoneLoginInvalidCode": "验证码格式错误",
142 - "@phoneLoginInvalidCode": { "description": "验证码格式错误 toast" },  
143 - 181 + "@phoneLoginInvalidCode": {
  182 + "description": "验证码格式错误 toast"
  183 + },
144 "todayHealthDataAuthTitle": "无法获取心率健康数据", 184 "todayHealthDataAuthTitle": "无法获取心率健康数据",
145 - "@todayHealthDataAuthTitle": { "description": "今日页健康数据授权卡片标题" }, 185 + "@todayHealthDataAuthTitle": {
  186 + "description": "今日页健康数据授权卡片标题"
  187 + },
146 "todayHealthDataAuthDescription": "DoubleFeel 需要授权访问你的健康数据,才能提供压力提醒、实时压力统计和健康建议;否则应用功能可能无法正常使用。请放心,你的健康数据仅存储在本地,不会上传到任何服务器。", 188 "todayHealthDataAuthDescription": "DoubleFeel 需要授权访问你的健康数据,才能提供压力提醒、实时压力统计和健康建议;否则应用功能可能无法正常使用。请放心,你的健康数据仅存储在本地,不会上传到任何服务器。",
147 - "@todayHealthDataAuthDescription": { "description": "今日页健康数据授权卡片说明" }, 189 + "@todayHealthDataAuthDescription": {
  190 + "description": "今日页健康数据授权卡片说明"
  191 + },
148 "todayHealthDataAuthAction": "授权访问健康数据", 192 "todayHealthDataAuthAction": "授权访问健康数据",
149 - "@todayHealthDataAuthAction": { "description": "今日页健康数据授权卡片按钮文案" } 193 + "@todayHealthDataAuthAction": {
  194 + "description": "今日页健康数据授权卡片按钮文案"
  195 + },
  196 + "measureYourHrvNow": "立即测量HRV",
  197 + "todayBottomSheetGotIt": "我知道了",
  198 + "todayFaqTitle": "常见问题",
  199 + "todayFaqSectionTitle": "DoubleFeel 常见问题",
  200 + "todayFaqLinkNoData": "APP或表盘有没有数据怎么办?",
  201 + "todayFaqLinkHrvRealtimeUpdate": "HRV数据如何实时更新?",
  202 + "todayFaqLinkWatchNoStatusNotification": "手表为什么无法收到状态通知?",
  203 + "todayFaqLinkWatchNoStatusAndInteractionNotification": "手表为什么无法收到状态和互动通知?",
  204 + "todayFaqLinkWatchFaceDataDelay": "手表表盘数据不更新或者有延迟?",
  205 + "todayFaqLinkWatchFaceBlackScreen": "手表表盘为什么会出现黑屏?",
  206 + "todayStressStatusTitle": "综合压力状态说明",
  207 + "todayHrvPrincipleTitle": "HRV测量原理",
  208 + "todayRealtimeStressTitle": "实时压力原理",
  209 + "todayStressStatusOverload": "压力过载",
  210 + "todayStressStatusCaution": "注意压力",
  211 + "todayStressStatusNormal": "状态正常",
  212 + "todayStressStatusExcellent": "状态优秀",
  213 + "todayStressStatusInsufficientData": "数据不足",
  214 + "todayStressStatusOverloadDescription": "当前 HRV 明显低于你的长期平均水平,可能意味着身体疲劳、压力过高或恢复不足。建议及时休息。",
  215 + "todayStressStatusCautionDescription": "当前 HRV 低于正常范围,身体可能正在积累压力,需要注意作息与恢复。",
  216 + "todayStressStatusNormalDescription": "当前身体状态处于你的正常波动范围内。",
  217 + "todayStressStatusExcellentDescription": "当前 HRV 高于近期平均水平,代表身体恢复与整体状态较好。",
  218 + "todayStressStatusInsufficientDataDescription": "当前可用数据不足,暂时无法准确判断压力状态。",
  219 + "todayHrvMeasurementIntro": "AppleWatch默认每2-5小时测量一次HRV,如果你希望立即手动进行测量,可以参考以下方法:",
  220 + "todayHrvMeasurementStep1": "1、戴紧AppleWatch,坐下来,保持心境平和",
  221 + "todayHrvMeasurementStep2": "2、打开AppleWatch上的「正念」里的「呼吸」",
  222 + "todayHrvMeasurementStep3": "3、保持平稳的呼吸,等待1-3分钟",
  223 + "todayHrvMeasurementStep4": "4、完成呼吸后,锁屏一次再解锁你的iPhone",
  224 + "todayHrvMeasurementStep5": "5、等待一分钟左右,StressWatch会收到你的数据并展示",
  225 + "todayHrvMeasurementHint": "提示:数据源来自AppleWatch,在测量之后可能存在延迟或是数据未能同步的情况。如若出现上述情况,请重新测量并等待数据读取。",
  226 + "todayHrvMeasurementWarning": "注意:需打开健康里的权限,同时关闭省电模式。",
  227 + "todayStressStatusWhatTitle": "什么是综合压力状态?",
  228 + "todayStressStatusWhatDescription1": "DoubleFeel 会结合你过去 30 天的 HRV(心率变异性)、静息心率以及当天的身体状态变化,综合评估你的整体压力水平。",
  229 + "todayStressStatusWhatDescription2": "由于 HRV 会随着情绪、运动、睡眠和疲劳不断波动,单次数据参考意义有限,因此我们更建议关注一整天的综合压力状态,让结果更稳定、更有参考价值。综合压力不仅能帮助你了解自己的身体状态,也能让亲密联系人更及时地关注你的变化。",
  230 + "todayStressStatusWhyHrvTitle": "为什么要参考 HRV(心率变异性)?",
  231 + "todayStressStatusWhyHrvDescription": "HRV 是衡量身体压力与恢复能力的重要指标。",
  232 + "todayStressStatusUsually": "通常来说:",
  233 + "todayStressStatusHrvHigher": "· HRV 越高,代表身体恢复状态越好",
  234 + "todayStressStatusHrvLower": "· HRV 越低,可能意味着疲劳、压力或睡眠不足",
  235 + "todayStressStatusHrvChangesFast": "· HRV 变化较快,更适合观察短时间内的身体状态变化。",
  236 + "todayStressStatusAppWatchDifferenceTitle": "手机 App 与 Apple Watch 显示的压力状态有什么区别?",
  237 + "todayStressStatusAppWatchDifferenceApp": "手机 App 首页显示的是当天的综合压力状态,会综合分析 HRV、静息心率与整体趋势。",
  238 + "todayStressStatusAppWatchDifferenceWatch": "Apple Watch 显示的是最近一次的实时压力状态,更适合快速查看当前身体变化。",
  239 + "todayStressStatusWaitingDataTitle": "为什么会出现“等待数据”?",
  240 + "todayStressStatusWaitingDataDescription1": "“等待数据”代表当前采集到的数据量不足,暂时无法生成可靠的压力评估。",
  241 + "todayStressStatusWaitingDataDescription2": "请继续佩戴 Apple Watch,等待系统自动采集数据。",
  242 + "todayStressStatusWaitingDataReasonsIntro": "可能原因包括:",
  243 + "todayStressStatusWaitingDataReason1": "1. HRV 数据采样不足",
  244 + "todayStressStatusWaitingDataReason2": "2. 缺少静息心率数据",
  245 + "todayStressStatusWaitingDataReason3": "3. Apple Watch 佩戴时间较短",
  246 + "todayStressStatusWaitingDataReason4": "4. Apple Health 权限未开启",
  247 + "todayHrvPrincipleHowMeasureTitle": "DoubleFeel 如何测量压力状态?",
  248 + "todayHrvPrincipleHowMeasureDescription1": "当你正常佩戴 Apple Watch 时,系统会自动采集你的心率数据,并同步至 Apple Health。",
  249 + "todayHrvPrincipleHowMeasureDescription2": "DoubleFeel 会基于这些数据计算 HRV(心率变异性)相关指标,用于评估你的身体压力与恢复状态。",
  250 + "todayHrvPrincipleHowMeasureDescription3": "HRV 对压力、疲劳、睡眠、情绪与身体恢复都非常敏感,因此它能够帮助我们更早发现身体状态变化。",
  251 + "todayHrvPrincipleHowMeasureDescription4": "为了让结果更准确,DoubleFeel 会将你当前的 HRV 状态与过去 30 天的个人平均水平进行对比,而不是直接与其他人比较。",
  252 + "todayRealtimeStressWhatTitle": "什么是实时压力?",
  253 + "todayRealtimeStressWhatDescription1": "实时压力是 DoubleFeel 根据你当前的 HRV、心率状态与个人历史数据变化,动态生成的身体压力指标。",
  254 + "todayRealtimeStressWhatDescription2": "压力值越高,代表你的身体状态相比平时偏离越明显,可能正处于疲劳、恢复不足或高压力状态。",
  255 + "todayRealtimeStressWhatDescription3": "它能够帮助你更快发现身体变化,并及时调整休息、运动与生活节奏。",
  256 + "todayRealtimeStressDivisionTitle": "实时压力如何划分?",
  257 + "todayRealtimeStressDivisionIntro": "实时压力以百分比形式展示:",
  258 + "todayRealtimeStressExcellentRange": "状态优秀:1%~20%",
  259 + "todayRealtimeStressNormalRange": "状态正常:21%~60%",
  260 + "todayRealtimeStressCautionRange": "注意压力:61%~80%",
  261 + "todayRealtimeStressOverloadRange": "压力过载:81%~100%",
  262 + "todayRealtimeStressExcellentDescription": "身体恢复状态较好,整体较放松。",
  263 + "todayRealtimeStressNormalDescription": "身体处于正常波动范围。",
  264 + "todayRealtimeStressCautionDescription": "身体可能正在积累压力,需要适当休息与恢复。",
  265 + "todayRealtimeStressOverloadDescription": "身体压力明显偏高,建议减少负荷、注意睡眠与恢复。",
  266 + "todayRealtimeStressDivisionBaseline": "以上区间会结合你的个人基线动态调整,不同用户之间并不直接比较。",
  267 + "todayRealtimeStressDivisionAwake": "此外,实时压力主要反映清醒状态下的身体压力变化。",
  268 + "todayRealtimeStressLowBetterTitle": "实时压力越低越好吗?",
  269 + "todayRealtimeStressLowBetterNo": "并不一定。",
  270 + "todayRealtimeStressLowBetterType": "身体压力分为“正常压力”与“异常压力”。",
  271 + "todayRealtimeStressLowBetterExample": "例如:运动期间或运动后,实时压力短时间升高属于正常恢复反应;工作专注、情绪兴奋时,压力也可能暂时升高,这些都属于正常的身体调节。",
  272 + "todayRealtimeStressLowBetterHighStress": "但如果在静息、久坐或睡眠不足的情况下,压力长期偏高,则可能意味着身体疲劳、心理压力较大、睡眠恢复不足、运动恢复不充分、摄入过多咖啡因、酒精或刺激物、身体可能处于不适状态。",
  273 + "todayRealtimeStressLowBetterTrend": "DoubleFeel 更关注的是你的长期变化趋势,而不是单次波动。",
  274 + "todayRealtimeStressScenarioTitle": "HRV 与实时压力适用场景?",
  275 + "todayRealtimeStressScenarioHrvDefault": "在 Apple Watch 的默认设置下,HRV 每 2~5 小时更新一次。",
  276 + "todayRealtimeStressScenarioRegionLimit": "在部分地区,由于 Apple Watch 的呼吸功能受限,HRV 的更新频率可能会受到影响,并且开启呼吸功能后也会消耗更多电量。",
  277 + "todayRealtimeStressScenarioIntro": "为了解决 HRV 更新间隔较长的问题,DoubleFeel 设计了实时压力功能:",
  278 + "todayRealtimeStressScenarioUpdateEvery6Min": "· 实时压力每 6 分钟更新一次",
  279 + "todayRealtimeStressScenarioTimely": "· 可以更及时地反映身体状态变化",
  280 + "todayRealtimeStressScenarioConsistentTrend": "· 在大多数情况下,实时压力趋势与 HRV 趋势是一致的",
  281 + "todayRealtimeStressScenarioSummary": "这样用户既能获得 HRV 的长期趋势,也能通过实时压力获得短时身体状态的参考。",
  282 + "todayFaqNoDataTitle": "APP或表盘有没有数据怎么办?",
  283 + "todayFaqNoDataDescription1": "1. 确认苹果手表系统在10.0以上,手机系统在14以上,系统版本可在「关于本机」内查看。",
  284 + "todayFaqNoDataDescription2": "2. 确认是否开启所有权限:手机「健康」-「共享」-「app」-「DoubleFeel」-「打开所有权限」。",
  285 + "todayFaqNoDataDescription3": "3. 确认设备是否处于省电模式、低电量状态或手表佩戴未贴紧,以上情况会影响手表数据采集。",
  286 + "todayFaqContactPrefix": "如以上均检查无问题,可以",
  287 + "todayFaqContactAction": "联系我们",
  288 + "todayFaqContactSuffix": "。",
  289 + "todayFaqWatchNoNotificationTitle": "手表无法收到状态通知?",
  290 + "todayFaqWatchNoNotificationDescription1": "苹果手表和手机的通知展示有优先级:当手机已解锁并亮屏时,通知只会在手机端展示,不会在手表上出现。",
  291 + "todayFaqWatchNoNotificationDescription2": "若压力数据可正常显示和自动更新,但手表未收到通知,可尝试以下操作:",
  292 + "todayFaqWatchNoNotificationCheckPhoneNotification": "1. 检查手机是否打开通知(设置-DoubleFeel-通知)。",
  293 + "todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh": "2. 检查手机是否打开后台App刷新(设置-DoubleFeel-后台App刷新)。",
  294 + "todayFaqWatchNoNotificationCheckWatchBackgroundRefresh": "3. 检查手表是否打开后台App刷新(设置-通用-后台App刷新,并确保DoubleFeel开启)。",
  295 + "todayFaqWatchNoNotificationCheckModes": "4. 确保未处于低电量/专注/勿扰/剧院/睡眠等模式。",
  296 + "todayFaqWatchNoNotificationReinstall": "5. 重装DoubleFeel 并重启AppleWatch与iPhone。",
  297 + "todayFaqWatchFaceDelayTitle": "手表表盘数据不更新或有延迟?",
  298 + "todayFaqWatchFaceDelayDescription1": "由于苹果系统限制,所有手表表盘(第三方或官方)都会存在几分钟至半小时的延迟,开发者无法控制刷新频率。",
  299 + "todayFaqWatchFaceDelayIfOverOneHour": "若手机数据刷新后超过1小时表盘仍未更新:",
  300 + "todayFaqWatchFaceDelayOpenWatchApp": "请在手表上手动打开DoubleFeel,等待约1分钟。",
  301 + "todayFaqWatchFaceDelayIfStill": "若仍未更新:",
  302 + "todayFaqWatchFaceDelayRestartApp": "关闭DoubleFeel后台进程并重启。",
  303 + "todayFaqWatchFaceDelayCheckIntro": "仍无效时,请检查:",
  304 + "todayFaqWatchFaceDelayCheckData": "· 手机和手表app是否可正常看到HRV数据。",
  305 + "todayFaqWatchFaceDelayCheckPhoneHealth": "· 确保手机端「iOS设置-隐私与安全性-健康-DoubleFeel」全部授权。",
  306 + "todayFaqWatchFaceDelayCheckWatchHealth": "· 确保手表端「设置-健康-数据来源、App和服务-DoubleFeel」全部授权。",
  307 + "todayFaqWatchFaceDelayCheckBackgroundRefresh": "· 确认AppleWatch-设置-通用-后台App刷新中DoubleFeel已开启。",
  308 + "todayFaqWatchFaceDelayRestartWatch": "· 若仍未自动刷新,请重启手表。长时间运行或后台占用过高可能导致表盘暂停更新。",
  309 + "todayFaqWatchFaceBlackScreenTitle": "手表表盘出现黑屏?",
  310 + "todayFaqWatchFaceBlackScreenDescription": "若添加专属互动表盘后出现黑屏(仅显示时间和日期),可长按表盘,点击「编辑」,左滑至「复杂功能」,选择 DoubleFeel,然后按需选择各组件重新添加。"
150 } 311 }
@@ -758,6 +758,696 @@ abstract class AppLocalizations { @@ -758,6 +758,696 @@ abstract class AppLocalizations {
758 /// In zh, this message translates to: 758 /// In zh, this message translates to:
759 /// **'授权访问健康数据'** 759 /// **'授权访问健康数据'**
760 String get todayHealthDataAuthAction; 760 String get todayHealthDataAuthAction;
  761 +
  762 + /// No description provided for @measureYourHrvNow.
  763 + ///
  764 + /// In zh, this message translates to:
  765 + /// **'立即测量HRV'**
  766 + String get measureYourHrvNow;
  767 +
  768 + /// No description provided for @todayBottomSheetGotIt.
  769 + ///
  770 + /// In zh, this message translates to:
  771 + /// **'我知道了'**
  772 + String get todayBottomSheetGotIt;
  773 +
  774 + /// No description provided for @todayFaqTitle.
  775 + ///
  776 + /// In zh, this message translates to:
  777 + /// **'常见问题'**
  778 + String get todayFaqTitle;
  779 +
  780 + /// No description provided for @todayFaqSectionTitle.
  781 + ///
  782 + /// In zh, this message translates to:
  783 + /// **'DoubleFeel 常见问题'**
  784 + String get todayFaqSectionTitle;
  785 +
  786 + /// No description provided for @todayFaqLinkNoData.
  787 + ///
  788 + /// In zh, this message translates to:
  789 + /// **'APP或表盘有没有数据怎么办?'**
  790 + String get todayFaqLinkNoData;
  791 +
  792 + /// No description provided for @todayFaqLinkHrvRealtimeUpdate.
  793 + ///
  794 + /// In zh, this message translates to:
  795 + /// **'HRV数据如何实时更新?'**
  796 + String get todayFaqLinkHrvRealtimeUpdate;
  797 +
  798 + /// No description provided for @todayFaqLinkWatchNoStatusNotification.
  799 + ///
  800 + /// In zh, this message translates to:
  801 + /// **'手表为什么无法收到状态通知?'**
  802 + String get todayFaqLinkWatchNoStatusNotification;
  803 +
  804 + /// No description provided for @todayFaqLinkWatchNoStatusAndInteractionNotification.
  805 + ///
  806 + /// In zh, this message translates to:
  807 + /// **'手表为什么无法收到状态和互动通知?'**
  808 + String get todayFaqLinkWatchNoStatusAndInteractionNotification;
  809 +
  810 + /// No description provided for @todayFaqLinkWatchFaceDataDelay.
  811 + ///
  812 + /// In zh, this message translates to:
  813 + /// **'手表表盘数据不更新或者有延迟?'**
  814 + String get todayFaqLinkWatchFaceDataDelay;
  815 +
  816 + /// No description provided for @todayFaqLinkWatchFaceBlackScreen.
  817 + ///
  818 + /// In zh, this message translates to:
  819 + /// **'手表表盘为什么会出现黑屏?'**
  820 + String get todayFaqLinkWatchFaceBlackScreen;
  821 +
  822 + /// No description provided for @todayStressStatusTitle.
  823 + ///
  824 + /// In zh, this message translates to:
  825 + /// **'综合压力状态说明'**
  826 + String get todayStressStatusTitle;
  827 +
  828 + /// No description provided for @todayHrvPrincipleTitle.
  829 + ///
  830 + /// In zh, this message translates to:
  831 + /// **'HRV测量原理'**
  832 + String get todayHrvPrincipleTitle;
  833 +
  834 + /// No description provided for @todayRealtimeStressTitle.
  835 + ///
  836 + /// In zh, this message translates to:
  837 + /// **'实时压力原理'**
  838 + String get todayRealtimeStressTitle;
  839 +
  840 + /// No description provided for @todayStressStatusOverload.
  841 + ///
  842 + /// In zh, this message translates to:
  843 + /// **'压力过载'**
  844 + String get todayStressStatusOverload;
  845 +
  846 + /// No description provided for @todayStressStatusCaution.
  847 + ///
  848 + /// In zh, this message translates to:
  849 + /// **'注意压力'**
  850 + String get todayStressStatusCaution;
  851 +
  852 + /// No description provided for @todayStressStatusNormal.
  853 + ///
  854 + /// In zh, this message translates to:
  855 + /// **'状态正常'**
  856 + String get todayStressStatusNormal;
  857 +
  858 + /// No description provided for @todayStressStatusExcellent.
  859 + ///
  860 + /// In zh, this message translates to:
  861 + /// **'状态优秀'**
  862 + String get todayStressStatusExcellent;
  863 +
  864 + /// No description provided for @todayStressStatusInsufficientData.
  865 + ///
  866 + /// In zh, this message translates to:
  867 + /// **'数据不足'**
  868 + String get todayStressStatusInsufficientData;
  869 +
  870 + /// No description provided for @todayStressStatusOverloadDescription.
  871 + ///
  872 + /// In zh, this message translates to:
  873 + /// **'当前 HRV 明显低于你的长期平均水平,可能意味着身体疲劳、压力过高或恢复不足。建议及时休息。'**
  874 + String get todayStressStatusOverloadDescription;
  875 +
  876 + /// No description provided for @todayStressStatusCautionDescription.
  877 + ///
  878 + /// In zh, this message translates to:
  879 + /// **'当前 HRV 低于正常范围,身体可能正在积累压力,需要注意作息与恢复。'**
  880 + String get todayStressStatusCautionDescription;
  881 +
  882 + /// No description provided for @todayStressStatusNormalDescription.
  883 + ///
  884 + /// In zh, this message translates to:
  885 + /// **'当前身体状态处于你的正常波动范围内。'**
  886 + String get todayStressStatusNormalDescription;
  887 +
  888 + /// No description provided for @todayStressStatusExcellentDescription.
  889 + ///
  890 + /// In zh, this message translates to:
  891 + /// **'当前 HRV 高于近期平均水平,代表身体恢复与整体状态较好。'**
  892 + String get todayStressStatusExcellentDescription;
  893 +
  894 + /// No description provided for @todayStressStatusInsufficientDataDescription.
  895 + ///
  896 + /// In zh, this message translates to:
  897 + /// **'当前可用数据不足,暂时无法准确判断压力状态。'**
  898 + String get todayStressStatusInsufficientDataDescription;
  899 +
  900 + /// No description provided for @todayHrvMeasurementIntro.
  901 + ///
  902 + /// In zh, this message translates to:
  903 + /// **'AppleWatch默认每2-5小时测量一次HRV,如果你希望立即手动进行测量,可以参考以下方法:'**
  904 + String get todayHrvMeasurementIntro;
  905 +
  906 + /// No description provided for @todayHrvMeasurementStep1.
  907 + ///
  908 + /// In zh, this message translates to:
  909 + /// **'1、戴紧AppleWatch,坐下来,保持心境平和'**
  910 + String get todayHrvMeasurementStep1;
  911 +
  912 + /// No description provided for @todayHrvMeasurementStep2.
  913 + ///
  914 + /// In zh, this message translates to:
  915 + /// **'2、打开AppleWatch上的「正念」里的「呼吸」'**
  916 + String get todayHrvMeasurementStep2;
  917 +
  918 + /// No description provided for @todayHrvMeasurementStep3.
  919 + ///
  920 + /// In zh, this message translates to:
  921 + /// **'3、保持平稳的呼吸,等待1-3分钟'**
  922 + String get todayHrvMeasurementStep3;
  923 +
  924 + /// No description provided for @todayHrvMeasurementStep4.
  925 + ///
  926 + /// In zh, this message translates to:
  927 + /// **'4、完成呼吸后,锁屏一次再解锁你的iPhone'**
  928 + String get todayHrvMeasurementStep4;
  929 +
  930 + /// No description provided for @todayHrvMeasurementStep5.
  931 + ///
  932 + /// In zh, this message translates to:
  933 + /// **'5、等待一分钟左右,StressWatch会收到你的数据并展示'**
  934 + String get todayHrvMeasurementStep5;
  935 +
  936 + /// No description provided for @todayHrvMeasurementHint.
  937 + ///
  938 + /// In zh, this message translates to:
  939 + /// **'提示:数据源来自AppleWatch,在测量之后可能存在延迟或是数据未能同步的情况。如若出现上述情况,请重新测量并等待数据读取。'**
  940 + String get todayHrvMeasurementHint;
  941 +
  942 + /// No description provided for @todayHrvMeasurementWarning.
  943 + ///
  944 + /// In zh, this message translates to:
  945 + /// **'注意:需打开健康里的权限,同时关闭省电模式。'**
  946 + String get todayHrvMeasurementWarning;
  947 +
  948 + /// No description provided for @todayStressStatusWhatTitle.
  949 + ///
  950 + /// In zh, this message translates to:
  951 + /// **'什么是综合压力状态?'**
  952 + String get todayStressStatusWhatTitle;
  953 +
  954 + /// No description provided for @todayStressStatusWhatDescription1.
  955 + ///
  956 + /// In zh, this message translates to:
  957 + /// **'DoubleFeel 会结合你过去 30 天的 HRV(心率变异性)、静息心率以及当天的身体状态变化,综合评估你的整体压力水平。'**
  958 + String get todayStressStatusWhatDescription1;
  959 +
  960 + /// No description provided for @todayStressStatusWhatDescription2.
  961 + ///
  962 + /// In zh, this message translates to:
  963 + /// **'由于 HRV 会随着情绪、运动、睡眠和疲劳不断波动,单次数据参考意义有限,因此我们更建议关注一整天的综合压力状态,让结果更稳定、更有参考价值。综合压力不仅能帮助你了解自己的身体状态,也能让亲密联系人更及时地关注你的变化。'**
  964 + String get todayStressStatusWhatDescription2;
  965 +
  966 + /// No description provided for @todayStressStatusWhyHrvTitle.
  967 + ///
  968 + /// In zh, this message translates to:
  969 + /// **'为什么要参考 HRV(心率变异性)?'**
  970 + String get todayStressStatusWhyHrvTitle;
  971 +
  972 + /// No description provided for @todayStressStatusWhyHrvDescription.
  973 + ///
  974 + /// In zh, this message translates to:
  975 + /// **'HRV 是衡量身体压力与恢复能力的重要指标。'**
  976 + String get todayStressStatusWhyHrvDescription;
  977 +
  978 + /// No description provided for @todayStressStatusUsually.
  979 + ///
  980 + /// In zh, this message translates to:
  981 + /// **'通常来说:'**
  982 + String get todayStressStatusUsually;
  983 +
  984 + /// No description provided for @todayStressStatusHrvHigher.
  985 + ///
  986 + /// In zh, this message translates to:
  987 + /// **'· HRV 越高,代表身体恢复状态越好'**
  988 + String get todayStressStatusHrvHigher;
  989 +
  990 + /// No description provided for @todayStressStatusHrvLower.
  991 + ///
  992 + /// In zh, this message translates to:
  993 + /// **'· HRV 越低,可能意味着疲劳、压力或睡眠不足'**
  994 + String get todayStressStatusHrvLower;
  995 +
  996 + /// No description provided for @todayStressStatusHrvChangesFast.
  997 + ///
  998 + /// In zh, this message translates to:
  999 + /// **'· HRV 变化较快,更适合观察短时间内的身体状态变化。'**
  1000 + String get todayStressStatusHrvChangesFast;
  1001 +
  1002 + /// No description provided for @todayStressStatusAppWatchDifferenceTitle.
  1003 + ///
  1004 + /// In zh, this message translates to:
  1005 + /// **'手机 App 与 Apple Watch 显示的压力状态有什么区别?'**
  1006 + String get todayStressStatusAppWatchDifferenceTitle;
  1007 +
  1008 + /// No description provided for @todayStressStatusAppWatchDifferenceApp.
  1009 + ///
  1010 + /// In zh, this message translates to:
  1011 + /// **'手机 App 首页显示的是当天的综合压力状态,会综合分析 HRV、静息心率与整体趋势。'**
  1012 + String get todayStressStatusAppWatchDifferenceApp;
  1013 +
  1014 + /// No description provided for @todayStressStatusAppWatchDifferenceWatch.
  1015 + ///
  1016 + /// In zh, this message translates to:
  1017 + /// **'Apple Watch 显示的是最近一次的实时压力状态,更适合快速查看当前身体变化。'**
  1018 + String get todayStressStatusAppWatchDifferenceWatch;
  1019 +
  1020 + /// No description provided for @todayStressStatusWaitingDataTitle.
  1021 + ///
  1022 + /// In zh, this message translates to:
  1023 + /// **'为什么会出现“等待数据”?'**
  1024 + String get todayStressStatusWaitingDataTitle;
  1025 +
  1026 + /// No description provided for @todayStressStatusWaitingDataDescription1.
  1027 + ///
  1028 + /// In zh, this message translates to:
  1029 + /// **'“等待数据”代表当前采集到的数据量不足,暂时无法生成可靠的压力评估。'**
  1030 + String get todayStressStatusWaitingDataDescription1;
  1031 +
  1032 + /// No description provided for @todayStressStatusWaitingDataDescription2.
  1033 + ///
  1034 + /// In zh, this message translates to:
  1035 + /// **'请继续佩戴 Apple Watch,等待系统自动采集数据。'**
  1036 + String get todayStressStatusWaitingDataDescription2;
  1037 +
  1038 + /// No description provided for @todayStressStatusWaitingDataReasonsIntro.
  1039 + ///
  1040 + /// In zh, this message translates to:
  1041 + /// **'可能原因包括:'**
  1042 + String get todayStressStatusWaitingDataReasonsIntro;
  1043 +
  1044 + /// No description provided for @todayStressStatusWaitingDataReason1.
  1045 + ///
  1046 + /// In zh, this message translates to:
  1047 + /// **'1. HRV 数据采样不足'**
  1048 + String get todayStressStatusWaitingDataReason1;
  1049 +
  1050 + /// No description provided for @todayStressStatusWaitingDataReason2.
  1051 + ///
  1052 + /// In zh, this message translates to:
  1053 + /// **'2. 缺少静息心率数据'**
  1054 + String get todayStressStatusWaitingDataReason2;
  1055 +
  1056 + /// No description provided for @todayStressStatusWaitingDataReason3.
  1057 + ///
  1058 + /// In zh, this message translates to:
  1059 + /// **'3. Apple Watch 佩戴时间较短'**
  1060 + String get todayStressStatusWaitingDataReason3;
  1061 +
  1062 + /// No description provided for @todayStressStatusWaitingDataReason4.
  1063 + ///
  1064 + /// In zh, this message translates to:
  1065 + /// **'4. Apple Health 权限未开启'**
  1066 + String get todayStressStatusWaitingDataReason4;
  1067 +
  1068 + /// No description provided for @todayHrvPrincipleHowMeasureTitle.
  1069 + ///
  1070 + /// In zh, this message translates to:
  1071 + /// **'DoubleFeel 如何测量压力状态?'**
  1072 + String get todayHrvPrincipleHowMeasureTitle;
  1073 +
  1074 + /// No description provided for @todayHrvPrincipleHowMeasureDescription1.
  1075 + ///
  1076 + /// In zh, this message translates to:
  1077 + /// **'当你正常佩戴 Apple Watch 时,系统会自动采集你的心率数据,并同步至 Apple Health。'**
  1078 + String get todayHrvPrincipleHowMeasureDescription1;
  1079 +
  1080 + /// No description provided for @todayHrvPrincipleHowMeasureDescription2.
  1081 + ///
  1082 + /// In zh, this message translates to:
  1083 + /// **'DoubleFeel 会基于这些数据计算 HRV(心率变异性)相关指标,用于评估你的身体压力与恢复状态。'**
  1084 + String get todayHrvPrincipleHowMeasureDescription2;
  1085 +
  1086 + /// No description provided for @todayHrvPrincipleHowMeasureDescription3.
  1087 + ///
  1088 + /// In zh, this message translates to:
  1089 + /// **'HRV 对压力、疲劳、睡眠、情绪与身体恢复都非常敏感,因此它能够帮助我们更早发现身体状态变化。'**
  1090 + String get todayHrvPrincipleHowMeasureDescription3;
  1091 +
  1092 + /// No description provided for @todayHrvPrincipleHowMeasureDescription4.
  1093 + ///
  1094 + /// In zh, this message translates to:
  1095 + /// **'为了让结果更准确,DoubleFeel 会将你当前的 HRV 状态与过去 30 天的个人平均水平进行对比,而不是直接与其他人比较。'**
  1096 + String get todayHrvPrincipleHowMeasureDescription4;
  1097 +
  1098 + /// No description provided for @todayRealtimeStressWhatTitle.
  1099 + ///
  1100 + /// In zh, this message translates to:
  1101 + /// **'什么是实时压力?'**
  1102 + String get todayRealtimeStressWhatTitle;
  1103 +
  1104 + /// No description provided for @todayRealtimeStressWhatDescription1.
  1105 + ///
  1106 + /// In zh, this message translates to:
  1107 + /// **'实时压力是 DoubleFeel 根据你当前的 HRV、心率状态与个人历史数据变化,动态生成的身体压力指标。'**
  1108 + String get todayRealtimeStressWhatDescription1;
  1109 +
  1110 + /// No description provided for @todayRealtimeStressWhatDescription2.
  1111 + ///
  1112 + /// In zh, this message translates to:
  1113 + /// **'压力值越高,代表你的身体状态相比平时偏离越明显,可能正处于疲劳、恢复不足或高压力状态。'**
  1114 + String get todayRealtimeStressWhatDescription2;
  1115 +
  1116 + /// No description provided for @todayRealtimeStressWhatDescription3.
  1117 + ///
  1118 + /// In zh, this message translates to:
  1119 + /// **'它能够帮助你更快发现身体变化,并及时调整休息、运动与生活节奏。'**
  1120 + String get todayRealtimeStressWhatDescription3;
  1121 +
  1122 + /// No description provided for @todayRealtimeStressDivisionTitle.
  1123 + ///
  1124 + /// In zh, this message translates to:
  1125 + /// **'实时压力如何划分?'**
  1126 + String get todayRealtimeStressDivisionTitle;
  1127 +
  1128 + /// No description provided for @todayRealtimeStressDivisionIntro.
  1129 + ///
  1130 + /// In zh, this message translates to:
  1131 + /// **'实时压力以百分比形式展示:'**
  1132 + String get todayRealtimeStressDivisionIntro;
  1133 +
  1134 + /// No description provided for @todayRealtimeStressExcellentRange.
  1135 + ///
  1136 + /// In zh, this message translates to:
  1137 + /// **'状态优秀:1%~20%'**
  1138 + String get todayRealtimeStressExcellentRange;
  1139 +
  1140 + /// No description provided for @todayRealtimeStressNormalRange.
  1141 + ///
  1142 + /// In zh, this message translates to:
  1143 + /// **'状态正常:21%~60%'**
  1144 + String get todayRealtimeStressNormalRange;
  1145 +
  1146 + /// No description provided for @todayRealtimeStressCautionRange.
  1147 + ///
  1148 + /// In zh, this message translates to:
  1149 + /// **'注意压力:61%~80%'**
  1150 + String get todayRealtimeStressCautionRange;
  1151 +
  1152 + /// No description provided for @todayRealtimeStressOverloadRange.
  1153 + ///
  1154 + /// In zh, this message translates to:
  1155 + /// **'压力过载:81%~100%'**
  1156 + String get todayRealtimeStressOverloadRange;
  1157 +
  1158 + /// No description provided for @todayRealtimeStressExcellentDescription.
  1159 + ///
  1160 + /// In zh, this message translates to:
  1161 + /// **'身体恢复状态较好,整体较放松。'**
  1162 + String get todayRealtimeStressExcellentDescription;
  1163 +
  1164 + /// No description provided for @todayRealtimeStressNormalDescription.
  1165 + ///
  1166 + /// In zh, this message translates to:
  1167 + /// **'身体处于正常波动范围。'**
  1168 + String get todayRealtimeStressNormalDescription;
  1169 +
  1170 + /// No description provided for @todayRealtimeStressCautionDescription.
  1171 + ///
  1172 + /// In zh, this message translates to:
  1173 + /// **'身体可能正在积累压力,需要适当休息与恢复。'**
  1174 + String get todayRealtimeStressCautionDescription;
  1175 +
  1176 + /// No description provided for @todayRealtimeStressOverloadDescription.
  1177 + ///
  1178 + /// In zh, this message translates to:
  1179 + /// **'身体压力明显偏高,建议减少负荷、注意睡眠与恢复。'**
  1180 + String get todayRealtimeStressOverloadDescription;
  1181 +
  1182 + /// No description provided for @todayRealtimeStressDivisionBaseline.
  1183 + ///
  1184 + /// In zh, this message translates to:
  1185 + /// **'以上区间会结合你的个人基线动态调整,不同用户之间并不直接比较。'**
  1186 + String get todayRealtimeStressDivisionBaseline;
  1187 +
  1188 + /// No description provided for @todayRealtimeStressDivisionAwake.
  1189 + ///
  1190 + /// In zh, this message translates to:
  1191 + /// **'此外,实时压力主要反映清醒状态下的身体压力变化。'**
  1192 + String get todayRealtimeStressDivisionAwake;
  1193 +
  1194 + /// No description provided for @todayRealtimeStressLowBetterTitle.
  1195 + ///
  1196 + /// In zh, this message translates to:
  1197 + /// **'实时压力越低越好吗?'**
  1198 + String get todayRealtimeStressLowBetterTitle;
  1199 +
  1200 + /// No description provided for @todayRealtimeStressLowBetterNo.
  1201 + ///
  1202 + /// In zh, this message translates to:
  1203 + /// **'并不一定。'**
  1204 + String get todayRealtimeStressLowBetterNo;
  1205 +
  1206 + /// No description provided for @todayRealtimeStressLowBetterType.
  1207 + ///
  1208 + /// In zh, this message translates to:
  1209 + /// **'身体压力分为“正常压力”与“异常压力”。'**
  1210 + String get todayRealtimeStressLowBetterType;
  1211 +
  1212 + /// No description provided for @todayRealtimeStressLowBetterExample.
  1213 + ///
  1214 + /// In zh, this message translates to:
  1215 + /// **'例如:运动期间或运动后,实时压力短时间升高属于正常恢复反应;工作专注、情绪兴奋时,压力也可能暂时升高,这些都属于正常的身体调节。'**
  1216 + String get todayRealtimeStressLowBetterExample;
  1217 +
  1218 + /// No description provided for @todayRealtimeStressLowBetterHighStress.
  1219 + ///
  1220 + /// In zh, this message translates to:
  1221 + /// **'但如果在静息、久坐或睡眠不足的情况下,压力长期偏高,则可能意味着身体疲劳、心理压力较大、睡眠恢复不足、运动恢复不充分、摄入过多咖啡因、酒精或刺激物、身体可能处于不适状态。'**
  1222 + String get todayRealtimeStressLowBetterHighStress;
  1223 +
  1224 + /// No description provided for @todayRealtimeStressLowBetterTrend.
  1225 + ///
  1226 + /// In zh, this message translates to:
  1227 + /// **'DoubleFeel 更关注的是你的长期变化趋势,而不是单次波动。'**
  1228 + String get todayRealtimeStressLowBetterTrend;
  1229 +
  1230 + /// No description provided for @todayRealtimeStressScenarioTitle.
  1231 + ///
  1232 + /// In zh, this message translates to:
  1233 + /// **'HRV 与实时压力适用场景?'**
  1234 + String get todayRealtimeStressScenarioTitle;
  1235 +
  1236 + /// No description provided for @todayRealtimeStressScenarioHrvDefault.
  1237 + ///
  1238 + /// In zh, this message translates to:
  1239 + /// **'在 Apple Watch 的默认设置下,HRV 每 2~5 小时更新一次。'**
  1240 + String get todayRealtimeStressScenarioHrvDefault;
  1241 +
  1242 + /// No description provided for @todayRealtimeStressScenarioRegionLimit.
  1243 + ///
  1244 + /// In zh, this message translates to:
  1245 + /// **'在部分地区,由于 Apple Watch 的呼吸功能受限,HRV 的更新频率可能会受到影响,并且开启呼吸功能后也会消耗更多电量。'**
  1246 + String get todayRealtimeStressScenarioRegionLimit;
  1247 +
  1248 + /// No description provided for @todayRealtimeStressScenarioIntro.
  1249 + ///
  1250 + /// In zh, this message translates to:
  1251 + /// **'为了解决 HRV 更新间隔较长的问题,DoubleFeel 设计了实时压力功能:'**
  1252 + String get todayRealtimeStressScenarioIntro;
  1253 +
  1254 + /// No description provided for @todayRealtimeStressScenarioUpdateEvery6Min.
  1255 + ///
  1256 + /// In zh, this message translates to:
  1257 + /// **'· 实时压力每 6 分钟更新一次'**
  1258 + String get todayRealtimeStressScenarioUpdateEvery6Min;
  1259 +
  1260 + /// No description provided for @todayRealtimeStressScenarioTimely.
  1261 + ///
  1262 + /// In zh, this message translates to:
  1263 + /// **'· 可以更及时地反映身体状态变化'**
  1264 + String get todayRealtimeStressScenarioTimely;
  1265 +
  1266 + /// No description provided for @todayRealtimeStressScenarioConsistentTrend.
  1267 + ///
  1268 + /// In zh, this message translates to:
  1269 + /// **'· 在大多数情况下,实时压力趋势与 HRV 趋势是一致的'**
  1270 + String get todayRealtimeStressScenarioConsistentTrend;
  1271 +
  1272 + /// No description provided for @todayRealtimeStressScenarioSummary.
  1273 + ///
  1274 + /// In zh, this message translates to:
  1275 + /// **'这样用户既能获得 HRV 的长期趋势,也能通过实时压力获得短时身体状态的参考。'**
  1276 + String get todayRealtimeStressScenarioSummary;
  1277 +
  1278 + /// No description provided for @todayFaqNoDataTitle.
  1279 + ///
  1280 + /// In zh, this message translates to:
  1281 + /// **'APP或表盘有没有数据怎么办?'**
  1282 + String get todayFaqNoDataTitle;
  1283 +
  1284 + /// No description provided for @todayFaqNoDataDescription1.
  1285 + ///
  1286 + /// In zh, this message translates to:
  1287 + /// **'1. 确认苹果手表系统在10.0以上,手机系统在14以上,系统版本可在「关于本机」内查看。'**
  1288 + String get todayFaqNoDataDescription1;
  1289 +
  1290 + /// No description provided for @todayFaqNoDataDescription2.
  1291 + ///
  1292 + /// In zh, this message translates to:
  1293 + /// **'2. 确认是否开启所有权限:手机「健康」-「共享」-「app」-「DoubleFeel」-「打开所有权限」。'**
  1294 + String get todayFaqNoDataDescription2;
  1295 +
  1296 + /// No description provided for @todayFaqNoDataDescription3.
  1297 + ///
  1298 + /// In zh, this message translates to:
  1299 + /// **'3. 确认设备是否处于省电模式、低电量状态或手表佩戴未贴紧,以上情况会影响手表数据采集。'**
  1300 + String get todayFaqNoDataDescription3;
  1301 +
  1302 + /// No description provided for @todayFaqContactPrefix.
  1303 + ///
  1304 + /// In zh, this message translates to:
  1305 + /// **'如以上均检查无问题,可以'**
  1306 + String get todayFaqContactPrefix;
  1307 +
  1308 + /// No description provided for @todayFaqContactAction.
  1309 + ///
  1310 + /// In zh, this message translates to:
  1311 + /// **'联系我们'**
  1312 + String get todayFaqContactAction;
  1313 +
  1314 + /// No description provided for @todayFaqContactSuffix.
  1315 + ///
  1316 + /// In zh, this message translates to:
  1317 + /// **'。'**
  1318 + String get todayFaqContactSuffix;
  1319 +
  1320 + /// No description provided for @todayFaqWatchNoNotificationTitle.
  1321 + ///
  1322 + /// In zh, this message translates to:
  1323 + /// **'手表无法收到状态通知?'**
  1324 + String get todayFaqWatchNoNotificationTitle;
  1325 +
  1326 + /// No description provided for @todayFaqWatchNoNotificationDescription1.
  1327 + ///
  1328 + /// In zh, this message translates to:
  1329 + /// **'苹果手表和手机的通知展示有优先级:当手机已解锁并亮屏时,通知只会在手机端展示,不会在手表上出现。'**
  1330 + String get todayFaqWatchNoNotificationDescription1;
  1331 +
  1332 + /// No description provided for @todayFaqWatchNoNotificationDescription2.
  1333 + ///
  1334 + /// In zh, this message translates to:
  1335 + /// **'若压力数据可正常显示和自动更新,但手表未收到通知,可尝试以下操作:'**
  1336 + String get todayFaqWatchNoNotificationDescription2;
  1337 +
  1338 + /// No description provided for @todayFaqWatchNoNotificationCheckPhoneNotification.
  1339 + ///
  1340 + /// In zh, this message translates to:
  1341 + /// **'1. 检查手机是否打开通知(设置-DoubleFeel-通知)。'**
  1342 + String get todayFaqWatchNoNotificationCheckPhoneNotification;
  1343 +
  1344 + /// No description provided for @todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh.
  1345 + ///
  1346 + /// In zh, this message translates to:
  1347 + /// **'2. 检查手机是否打开后台App刷新(设置-DoubleFeel-后台App刷新)。'**
  1348 + String get todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh;
  1349 +
  1350 + /// No description provided for @todayFaqWatchNoNotificationCheckWatchBackgroundRefresh.
  1351 + ///
  1352 + /// In zh, this message translates to:
  1353 + /// **'3. 检查手表是否打开后台App刷新(设置-通用-后台App刷新,并确保DoubleFeel开启)。'**
  1354 + String get todayFaqWatchNoNotificationCheckWatchBackgroundRefresh;
  1355 +
  1356 + /// No description provided for @todayFaqWatchNoNotificationCheckModes.
  1357 + ///
  1358 + /// In zh, this message translates to:
  1359 + /// **'4. 确保未处于低电量/专注/勿扰/剧院/睡眠等模式。'**
  1360 + String get todayFaqWatchNoNotificationCheckModes;
  1361 +
  1362 + /// No description provided for @todayFaqWatchNoNotificationReinstall.
  1363 + ///
  1364 + /// In zh, this message translates to:
  1365 + /// **'5. 重装DoubleFeel 并重启AppleWatch与iPhone。'**
  1366 + String get todayFaqWatchNoNotificationReinstall;
  1367 +
  1368 + /// No description provided for @todayFaqWatchFaceDelayTitle.
  1369 + ///
  1370 + /// In zh, this message translates to:
  1371 + /// **'手表表盘数据不更新或有延迟?'**
  1372 + String get todayFaqWatchFaceDelayTitle;
  1373 +
  1374 + /// No description provided for @todayFaqWatchFaceDelayDescription1.
  1375 + ///
  1376 + /// In zh, this message translates to:
  1377 + /// **'由于苹果系统限制,所有手表表盘(第三方或官方)都会存在几分钟至半小时的延迟,开发者无法控制刷新频率。'**
  1378 + String get todayFaqWatchFaceDelayDescription1;
  1379 +
  1380 + /// No description provided for @todayFaqWatchFaceDelayIfOverOneHour.
  1381 + ///
  1382 + /// In zh, this message translates to:
  1383 + /// **'若手机数据刷新后超过1小时表盘仍未更新:'**
  1384 + String get todayFaqWatchFaceDelayIfOverOneHour;
  1385 +
  1386 + /// No description provided for @todayFaqWatchFaceDelayOpenWatchApp.
  1387 + ///
  1388 + /// In zh, this message translates to:
  1389 + /// **'请在手表上手动打开DoubleFeel,等待约1分钟。'**
  1390 + String get todayFaqWatchFaceDelayOpenWatchApp;
  1391 +
  1392 + /// No description provided for @todayFaqWatchFaceDelayIfStill.
  1393 + ///
  1394 + /// In zh, this message translates to:
  1395 + /// **'若仍未更新:'**
  1396 + String get todayFaqWatchFaceDelayIfStill;
  1397 +
  1398 + /// No description provided for @todayFaqWatchFaceDelayRestartApp.
  1399 + ///
  1400 + /// In zh, this message translates to:
  1401 + /// **'关闭DoubleFeel后台进程并重启。'**
  1402 + String get todayFaqWatchFaceDelayRestartApp;
  1403 +
  1404 + /// No description provided for @todayFaqWatchFaceDelayCheckIntro.
  1405 + ///
  1406 + /// In zh, this message translates to:
  1407 + /// **'仍无效时,请检查:'**
  1408 + String get todayFaqWatchFaceDelayCheckIntro;
  1409 +
  1410 + /// No description provided for @todayFaqWatchFaceDelayCheckData.
  1411 + ///
  1412 + /// In zh, this message translates to:
  1413 + /// **'· 手机和手表app是否可正常看到HRV数据。'**
  1414 + String get todayFaqWatchFaceDelayCheckData;
  1415 +
  1416 + /// No description provided for @todayFaqWatchFaceDelayCheckPhoneHealth.
  1417 + ///
  1418 + /// In zh, this message translates to:
  1419 + /// **'· 确保手机端「iOS设置-隐私与安全性-健康-DoubleFeel」全部授权。'**
  1420 + String get todayFaqWatchFaceDelayCheckPhoneHealth;
  1421 +
  1422 + /// No description provided for @todayFaqWatchFaceDelayCheckWatchHealth.
  1423 + ///
  1424 + /// In zh, this message translates to:
  1425 + /// **'· 确保手表端「设置-健康-数据来源、App和服务-DoubleFeel」全部授权。'**
  1426 + String get todayFaqWatchFaceDelayCheckWatchHealth;
  1427 +
  1428 + /// No description provided for @todayFaqWatchFaceDelayCheckBackgroundRefresh.
  1429 + ///
  1430 + /// In zh, this message translates to:
  1431 + /// **'· 确认AppleWatch-设置-通用-后台App刷新中DoubleFeel已开启。'**
  1432 + String get todayFaqWatchFaceDelayCheckBackgroundRefresh;
  1433 +
  1434 + /// No description provided for @todayFaqWatchFaceDelayRestartWatch.
  1435 + ///
  1436 + /// In zh, this message translates to:
  1437 + /// **'· 若仍未自动刷新,请重启手表。长时间运行或后台占用过高可能导致表盘暂停更新。'**
  1438 + String get todayFaqWatchFaceDelayRestartWatch;
  1439 +
  1440 + /// No description provided for @todayFaqWatchFaceBlackScreenTitle.
  1441 + ///
  1442 + /// In zh, this message translates to:
  1443 + /// **'手表表盘出现黑屏?'**
  1444 + String get todayFaqWatchFaceBlackScreenTitle;
  1445 +
  1446 + /// No description provided for @todayFaqWatchFaceBlackScreenDescription.
  1447 + ///
  1448 + /// In zh, this message translates to:
  1449 + /// **'若添加专属互动表盘后出现黑屏(仅显示时间和日期),可长按表盘,点击「编辑」,左滑至「复杂功能」,选择 DoubleFeel,然后按需选择各组件重新添加。'**
  1450 + String get todayFaqWatchFaceBlackScreenDescription;
761 } 1451 }
762 1452
763 class _AppLocalizationsDelegate 1453 class _AppLocalizationsDelegate
@@ -364,4 +364,438 @@ class AppLocalizationsEn extends AppLocalizations { @@ -364,4 +364,438 @@ class AppLocalizationsEn extends AppLocalizations {
364 364
365 @override 365 @override
366 String get todayHealthDataAuthAction => 'Authorize health data access'; 366 String get todayHealthDataAuthAction => 'Authorize health data access';
  367 +
  368 + @override
  369 + String get measureYourHrvNow => 'Measure your HRV now';
  370 +
  371 + @override
  372 + String get todayBottomSheetGotIt => 'Got it';
  373 +
  374 + @override
  375 + String get todayFaqTitle => 'FAQ';
  376 +
  377 + @override
  378 + String get todayFaqSectionTitle => 'DoubleFeel FAQ';
  379 +
  380 + @override
  381 + String get todayFaqLinkNoData => 'What if the app or watch face has no data?';
  382 +
  383 + @override
  384 + String get todayFaqLinkHrvRealtimeUpdate =>
  385 + 'How can HRV data update in real time?';
  386 +
  387 + @override
  388 + String get todayFaqLinkWatchNoStatusNotification =>
  389 + 'Why can\'t my watch receive status notifications?';
  390 +
  391 + @override
  392 + String get todayFaqLinkWatchNoStatusAndInteractionNotification =>
  393 + 'Why can\'t my watch receive status and interaction notifications?';
  394 +
  395 + @override
  396 + String get todayFaqLinkWatchFaceDataDelay =>
  397 + 'Why is watch face data delayed or not updating?';
  398 +
  399 + @override
  400 + String get todayFaqLinkWatchFaceBlackScreen =>
  401 + 'Why does the watch face turn black?';
  402 +
  403 + @override
  404 + String get todayStressStatusTitle => 'Overall stress status';
  405 +
  406 + @override
  407 + String get todayHrvPrincipleTitle => 'How HRV is measured';
  408 +
  409 + @override
  410 + String get todayRealtimeStressTitle => 'How real-time stress works';
  411 +
  412 + @override
  413 + String get todayStressStatusOverload => 'Stress overload';
  414 +
  415 + @override
  416 + String get todayStressStatusCaution => 'Stress warning';
  417 +
  418 + @override
  419 + String get todayStressStatusNormal => 'Normal';
  420 +
  421 + @override
  422 + String get todayStressStatusExcellent => 'Excellent';
  423 +
  424 + @override
  425 + String get todayStressStatusInsufficientData => 'Insufficient data';
  426 +
  427 + @override
  428 + String get todayStressStatusOverloadDescription =>
  429 + 'Your current HRV is much lower than your long-term average, which may indicate fatigue, high stress, or insufficient recovery. Rest is recommended.';
  430 +
  431 + @override
  432 + String get todayStressStatusCautionDescription =>
  433 + 'Your current HRV is below the normal range, and your body may be accumulating stress. Pay attention to rest and recovery.';
  434 +
  435 + @override
  436 + String get todayStressStatusNormalDescription =>
  437 + 'Your current body state is within your normal fluctuation range.';
  438 +
  439 + @override
  440 + String get todayStressStatusExcellentDescription =>
  441 + 'Your current HRV is higher than your recent average, indicating better recovery and overall state.';
  442 +
  443 + @override
  444 + String get todayStressStatusInsufficientDataDescription =>
  445 + 'There is not enough available data to accurately assess your stress state yet.';
  446 +
  447 + @override
  448 + String get todayHrvMeasurementIntro =>
  449 + 'Apple Watch measures HRV every 2-5 hours by default. If you want to measure it manually right now, follow these steps:';
  450 +
  451 + @override
  452 + String get todayHrvMeasurementStep1 =>
  453 + '1. Wear your Apple Watch snugly, sit down, and stay calm';
  454 +
  455 + @override
  456 + String get todayHrvMeasurementStep2 =>
  457 + '2. Open Mindfulness on Apple Watch and start Breathe';
  458 +
  459 + @override
  460 + String get todayHrvMeasurementStep3 =>
  461 + '3. Keep breathing steadily and wait 1-3 minutes';
  462 +
  463 + @override
  464 + String get todayHrvMeasurementStep4 =>
  465 + '4. After breathing is complete, lock and unlock your iPhone once';
  466 +
  467 + @override
  468 + String get todayHrvMeasurementStep5 =>
  469 + '5. Wait about one minute. StressWatch will receive and display your data';
  470 +
  471 + @override
  472 + String get todayHrvMeasurementHint =>
  473 + 'Tip: Data comes from Apple Watch. After measurement, there may be delays or data may not sync immediately. If this happens, measure again and wait for the data to be read.';
  474 +
  475 + @override
  476 + String get todayHrvMeasurementWarning =>
  477 + 'Note: Health permissions must be enabled, and Low Power Mode must be turned off.';
  478 +
  479 + @override
  480 + String get todayStressStatusWhatTitle => 'What is overall stress status?';
  481 +
  482 + @override
  483 + String get todayStressStatusWhatDescription1 =>
  484 + 'DoubleFeel combines your HRV (heart rate variability), resting heart rate, and body-state changes from the past 30 days to assess your overall stress level.';
  485 +
  486 + @override
  487 + String get todayStressStatusWhatDescription2 =>
  488 + 'Because HRV fluctuates with emotions, exercise, sleep, and fatigue, a single reading has limited value. We recommend focusing on your overall stress status across the day, which is more stable and useful. It helps you understand your body state and helps close contacts notice changes in time.';
  489 +
  490 + @override
  491 + String get todayStressStatusWhyHrvTitle =>
  492 + 'Why use HRV (heart rate variability)?';
  493 +
  494 + @override
  495 + String get todayStressStatusWhyHrvDescription =>
  496 + 'HRV is an important metric for measuring body stress and recovery capacity.';
  497 +
  498 + @override
  499 + String get todayStressStatusUsually => 'In general:';
  500 +
  501 + @override
  502 + String get todayStressStatusHrvHigher =>
  503 + '· Higher HRV usually means better recovery';
  504 +
  505 + @override
  506 + String get todayStressStatusHrvLower =>
  507 + '· Lower HRV may indicate fatigue, stress, or insufficient sleep';
  508 +
  509 + @override
  510 + String get todayStressStatusHrvChangesFast =>
  511 + '· HRV changes quickly, making it useful for short-term body-state changes.';
  512 +
  513 + @override
  514 + String get todayStressStatusAppWatchDifferenceTitle =>
  515 + 'How are stress statuses on the phone app and Apple Watch different?';
  516 +
  517 + @override
  518 + String get todayStressStatusAppWatchDifferenceApp =>
  519 + 'The phone app home page shows the day\'s overall stress status, combining HRV, resting heart rate, and overall trends.';
  520 +
  521 + @override
  522 + String get todayStressStatusAppWatchDifferenceWatch =>
  523 + 'Apple Watch shows the most recent real-time stress status, which is better for quickly checking your current body changes.';
  524 +
  525 + @override
  526 + String get todayStressStatusWaitingDataTitle =>
  527 + 'Why does Waiting for data appear?';
  528 +
  529 + @override
  530 + String get todayStressStatusWaitingDataDescription1 =>
  531 + 'Waiting for data means the current amount of collected data is not enough to generate a reliable stress assessment.';
  532 +
  533 + @override
  534 + String get todayStressStatusWaitingDataDescription2 =>
  535 + 'Please keep wearing your Apple Watch and wait for the system to collect data automatically.';
  536 +
  537 + @override
  538 + String get todayStressStatusWaitingDataReasonsIntro =>
  539 + 'Possible reasons include:';
  540 +
  541 + @override
  542 + String get todayStressStatusWaitingDataReason1 => '1. Not enough HRV samples';
  543 +
  544 + @override
  545 + String get todayStressStatusWaitingDataReason2 =>
  546 + '2. Missing resting heart rate data';
  547 +
  548 + @override
  549 + String get todayStressStatusWaitingDataReason3 =>
  550 + '3. Apple Watch has not been worn long enough';
  551 +
  552 + @override
  553 + String get todayStressStatusWaitingDataReason4 =>
  554 + '4. Apple Health permissions are not enabled';
  555 +
  556 + @override
  557 + String get todayHrvPrincipleHowMeasureTitle =>
  558 + 'How does DoubleFeel measure stress status?';
  559 +
  560 + @override
  561 + String get todayHrvPrincipleHowMeasureDescription1 =>
  562 + 'When you wear Apple Watch normally, the system automatically collects your heart rate data and syncs it to Apple Health.';
  563 +
  564 + @override
  565 + String get todayHrvPrincipleHowMeasureDescription2 =>
  566 + 'DoubleFeel calculates HRV (heart rate variability) indicators based on this data to assess your body stress and recovery state.';
  567 +
  568 + @override
  569 + String get todayHrvPrincipleHowMeasureDescription3 =>
  570 + 'HRV is sensitive to stress, fatigue, sleep, emotions, and recovery, so it helps us notice body-state changes earlier.';
  571 +
  572 + @override
  573 + String get todayHrvPrincipleHowMeasureDescription4 =>
  574 + 'To make results more accurate, DoubleFeel compares your current HRV state with your own 30-day average instead of comparing it directly with other people.';
  575 +
  576 + @override
  577 + String get todayRealtimeStressWhatTitle => 'What is real-time stress?';
  578 +
  579 + @override
  580 + String get todayRealtimeStressWhatDescription1 =>
  581 + 'Real-time stress is a body stress indicator dynamically generated by DoubleFeel based on your current HRV, heart rate state, and changes in your personal history.';
  582 +
  583 + @override
  584 + String get todayRealtimeStressWhatDescription2 =>
  585 + 'A higher stress value means your body state is deviating more from your usual baseline and may reflect fatigue, insufficient recovery, or high stress.';
  586 +
  587 + @override
  588 + String get todayRealtimeStressWhatDescription3 =>
  589 + 'It helps you notice body changes faster and adjust rest, exercise, and daily rhythm in time.';
  590 +
  591 + @override
  592 + String get todayRealtimeStressDivisionTitle =>
  593 + 'How is real-time stress divided?';
  594 +
  595 + @override
  596 + String get todayRealtimeStressDivisionIntro =>
  597 + 'Real-time stress is shown as a percentage:';
  598 +
  599 + @override
  600 + String get todayRealtimeStressExcellentRange => 'Excellent: 1%-20%';
  601 +
  602 + @override
  603 + String get todayRealtimeStressNormalRange => 'Normal: 21%-60%';
  604 +
  605 + @override
  606 + String get todayRealtimeStressCautionRange => 'Stress warning: 61%-80%';
  607 +
  608 + @override
  609 + String get todayRealtimeStressOverloadRange => 'Stress overload: 81%-100%';
  610 +
  611 + @override
  612 + String get todayRealtimeStressExcellentDescription =>
  613 + 'Your recovery state is good and you are generally relaxed.';
  614 +
  615 + @override
  616 + String get todayRealtimeStressNormalDescription =>
  617 + 'Your body is within the normal fluctuation range.';
  618 +
  619 + @override
  620 + String get todayRealtimeStressCautionDescription =>
  621 + 'Your body may be accumulating stress and needs proper rest and recovery.';
  622 +
  623 + @override
  624 + String get todayRealtimeStressOverloadDescription =>
  625 + 'Your body stress is clearly high. Reduce load and pay attention to sleep and recovery.';
  626 +
  627 + @override
  628 + String get todayRealtimeStressDivisionBaseline =>
  629 + 'These ranges are adjusted dynamically based on your personal baseline and should not be directly compared between users.';
  630 +
  631 + @override
  632 + String get todayRealtimeStressDivisionAwake =>
  633 + 'Real-time stress mainly reflects body stress changes while awake.';
  634 +
  635 + @override
  636 + String get todayRealtimeStressLowBetterTitle =>
  637 + 'Is lower real-time stress always better?';
  638 +
  639 + @override
  640 + String get todayRealtimeStressLowBetterNo => 'Not necessarily.';
  641 +
  642 + @override
  643 + String get todayRealtimeStressLowBetterType =>
  644 + 'Body stress can be normal or abnormal.';
  645 +
  646 + @override
  647 + String get todayRealtimeStressLowBetterExample =>
  648 + 'For example, real-time stress rising briefly during or after exercise is a normal recovery response. It can also rise temporarily during focused work or emotional excitement, which are normal body adjustments.';
  649 +
  650 + @override
  651 + String get todayRealtimeStressLowBetterHighStress =>
  652 + 'But if stress remains high while resting, sitting for a long time, or after poor sleep, it may indicate physical fatigue, mental stress, insufficient sleep recovery, incomplete exercise recovery, too much caffeine, alcohol, stimulants, or possible discomfort.';
  653 +
  654 + @override
  655 + String get todayRealtimeStressLowBetterTrend =>
  656 + 'DoubleFeel focuses more on your long-term trend than on a single fluctuation.';
  657 +
  658 + @override
  659 + String get todayRealtimeStressScenarioTitle =>
  660 + 'When should HRV and real-time stress be used?';
  661 +
  662 + @override
  663 + String get todayRealtimeStressScenarioHrvDefault =>
  664 + 'With Apple Watch default settings, HRV updates every 2-5 hours.';
  665 +
  666 + @override
  667 + String get todayRealtimeStressScenarioRegionLimit =>
  668 + 'In some regions, Apple Watch breathing features may be limited, which can affect HRV update frequency. Turning on breathing features may also consume more battery.';
  669 +
  670 + @override
  671 + String get todayRealtimeStressScenarioIntro =>
  672 + 'To address the long interval between HRV updates, DoubleFeel designed real-time stress:';
  673 +
  674 + @override
  675 + String get todayRealtimeStressScenarioUpdateEvery6Min =>
  676 + '· Real-time stress updates every 6 minutes';
  677 +
  678 + @override
  679 + String get todayRealtimeStressScenarioTimely =>
  680 + '· It can reflect body-state changes more promptly';
  681 +
  682 + @override
  683 + String get todayRealtimeStressScenarioConsistentTrend =>
  684 + '· In most cases, the real-time stress trend is consistent with the HRV trend';
  685 +
  686 + @override
  687 + String get todayRealtimeStressScenarioSummary =>
  688 + 'This lets users see long-term HRV trends while also using real-time stress as a short-term body-state reference.';
  689 +
  690 + @override
  691 + String get todayFaqNoDataTitle =>
  692 + 'What if the app or watch face has no data?';
  693 +
  694 + @override
  695 + String get todayFaqNoDataDescription1 =>
  696 + '1. Confirm that Apple Watch is on watchOS 10.0 or above and iPhone is on iOS 14 or above. You can check system versions in About.';
  697 +
  698 + @override
  699 + String get todayFaqNoDataDescription2 =>
  700 + '2. Confirm all permissions are enabled: iPhone Health > Sharing > Apps > DoubleFeel > Turn On All Permissions.';
  701 +
  702 + @override
  703 + String get todayFaqNoDataDescription3 =>
  704 + '3. Confirm the device is not in Low Power Mode, low battery, or worn too loosely, as these can affect data collection.';
  705 +
  706 + @override
  707 + String get todayFaqContactPrefix =>
  708 + 'If everything above is correct, you can ';
  709 +
  710 + @override
  711 + String get todayFaqContactAction => 'contact us';
  712 +
  713 + @override
  714 + String get todayFaqContactSuffix => '.';
  715 +
  716 + @override
  717 + String get todayFaqWatchNoNotificationTitle =>
  718 + 'Watch cannot receive status notifications?';
  719 +
  720 + @override
  721 + String get todayFaqWatchNoNotificationDescription1 =>
  722 + 'Apple Watch and iPhone notifications have priority rules: when your iPhone is unlocked and the screen is on, notifications only appear on the phone and will not appear on the watch.';
  723 +
  724 + @override
  725 + String get todayFaqWatchNoNotificationDescription2 =>
  726 + 'If stress data displays and updates normally but your watch does not receive notifications, try the following:';
  727 +
  728 + @override
  729 + String get todayFaqWatchNoNotificationCheckPhoneNotification =>
  730 + '1. Check whether iPhone notifications are enabled (Settings > DoubleFeel > Notifications).';
  731 +
  732 + @override
  733 + String get todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh =>
  734 + '2. Check whether iPhone Background App Refresh is enabled (Settings > DoubleFeel > Background App Refresh).';
  735 +
  736 + @override
  737 + String get todayFaqWatchNoNotificationCheckWatchBackgroundRefresh =>
  738 + '3. Check whether Apple Watch Background App Refresh is enabled (Settings > General > Background App Refresh, and make sure DoubleFeel is enabled).';
  739 +
  740 + @override
  741 + String get todayFaqWatchNoNotificationCheckModes =>
  742 + '4. Make sure Low Power, Focus, Do Not Disturb, Theater, Sleep, and similar modes are off.';
  743 +
  744 + @override
  745 + String get todayFaqWatchNoNotificationReinstall =>
  746 + '5. Reinstall DoubleFeel and restart Apple Watch and iPhone.';
  747 +
  748 + @override
  749 + String get todayFaqWatchFaceDelayTitle =>
  750 + 'Watch face data not updating or delayed?';
  751 +
  752 + @override
  753 + String get todayFaqWatchFaceDelayDescription1 =>
  754 + 'Due to Apple system limits, all watch faces, third-party or official, may have delays from a few minutes to half an hour. Developers cannot control the refresh frequency.';
  755 +
  756 + @override
  757 + String get todayFaqWatchFaceDelayIfOverOneHour =>
  758 + 'If the phone data refreshes but the watch face still has not updated after more than 1 hour:';
  759 +
  760 + @override
  761 + String get todayFaqWatchFaceDelayOpenWatchApp =>
  762 + 'Manually open DoubleFeel on Apple Watch and wait about 1 minute.';
  763 +
  764 + @override
  765 + String get todayFaqWatchFaceDelayIfStill => 'If it still does not update:';
  766 +
  767 + @override
  768 + String get todayFaqWatchFaceDelayRestartApp =>
  769 + 'Close the DoubleFeel background process and restart it.';
  770 +
  771 + @override
  772 + String get todayFaqWatchFaceDelayCheckIntro =>
  773 + 'If it still does not work, check:';
  774 +
  775 + @override
  776 + String get todayFaqWatchFaceDelayCheckData =>
  777 + '· Whether both phone and watch apps can show HRV data normally.';
  778 +
  779 + @override
  780 + String get todayFaqWatchFaceDelayCheckPhoneHealth =>
  781 + '· Make sure all permissions are enabled on iPhone: iOS Settings > Privacy & Security > Health > DoubleFeel.';
  782 +
  783 + @override
  784 + String get todayFaqWatchFaceDelayCheckWatchHealth =>
  785 + '· Make sure all permissions are enabled on Apple Watch: Settings > Health > Data Sources & Access > DoubleFeel.';
  786 +
  787 + @override
  788 + String get todayFaqWatchFaceDelayCheckBackgroundRefresh =>
  789 + '· Confirm DoubleFeel is enabled in Apple Watch > Settings > General > Background App Refresh.';
  790 +
  791 + @override
  792 + String get todayFaqWatchFaceDelayRestartWatch =>
  793 + '· If it still does not refresh automatically, restart Apple Watch. Long runtimes or high background usage may cause watch face updates to pause.';
  794 +
  795 + @override
  796 + String get todayFaqWatchFaceBlackScreenTitle => 'Watch face turns black?';
  797 +
  798 + @override
  799 + String get todayFaqWatchFaceBlackScreenDescription =>
  800 + 'If the custom interactive watch face turns black after being added and only shows time and date, long-press the watch face, tap Edit, swipe left to Complications, choose DoubleFeel, and add each component again as needed.';
367 } 801 }
@@ -347,4 +347,397 @@ class AppLocalizationsZh extends AppLocalizations { @@ -347,4 +347,397 @@ class AppLocalizationsZh extends AppLocalizations {
347 347
348 @override 348 @override
349 String get todayHealthDataAuthAction => '授权访问健康数据'; 349 String get todayHealthDataAuthAction => '授权访问健康数据';
  350 +
  351 + @override
  352 + String get measureYourHrvNow => '立即测量HRV';
  353 +
  354 + @override
  355 + String get todayBottomSheetGotIt => '我知道了';
  356 +
  357 + @override
  358 + String get todayFaqTitle => '常见问题';
  359 +
  360 + @override
  361 + String get todayFaqSectionTitle => 'DoubleFeel 常见问题';
  362 +
  363 + @override
  364 + String get todayFaqLinkNoData => 'APP或表盘有没有数据怎么办?';
  365 +
  366 + @override
  367 + String get todayFaqLinkHrvRealtimeUpdate => 'HRV数据如何实时更新?';
  368 +
  369 + @override
  370 + String get todayFaqLinkWatchNoStatusNotification => '手表为什么无法收到状态通知?';
  371 +
  372 + @override
  373 + String get todayFaqLinkWatchNoStatusAndInteractionNotification =>
  374 + '手表为什么无法收到状态和互动通知?';
  375 +
  376 + @override
  377 + String get todayFaqLinkWatchFaceDataDelay => '手表表盘数据不更新或者有延迟?';
  378 +
  379 + @override
  380 + String get todayFaqLinkWatchFaceBlackScreen => '手表表盘为什么会出现黑屏?';
  381 +
  382 + @override
  383 + String get todayStressStatusTitle => '综合压力状态说明';
  384 +
  385 + @override
  386 + String get todayHrvPrincipleTitle => 'HRV测量原理';
  387 +
  388 + @override
  389 + String get todayRealtimeStressTitle => '实时压力原理';
  390 +
  391 + @override
  392 + String get todayStressStatusOverload => '压力过载';
  393 +
  394 + @override
  395 + String get todayStressStatusCaution => '注意压力';
  396 +
  397 + @override
  398 + String get todayStressStatusNormal => '状态正常';
  399 +
  400 + @override
  401 + String get todayStressStatusExcellent => '状态优秀';
  402 +
  403 + @override
  404 + String get todayStressStatusInsufficientData => '数据不足';
  405 +
  406 + @override
  407 + String get todayStressStatusOverloadDescription =>
  408 + '当前 HRV 明显低于你的长期平均水平,可能意味着身体疲劳、压力过高或恢复不足。建议及时休息。';
  409 +
  410 + @override
  411 + String get todayStressStatusCautionDescription =>
  412 + '当前 HRV 低于正常范围,身体可能正在积累压力,需要注意作息与恢复。';
  413 +
  414 + @override
  415 + String get todayStressStatusNormalDescription => '当前身体状态处于你的正常波动范围内。';
  416 +
  417 + @override
  418 + String get todayStressStatusExcellentDescription =>
  419 + '当前 HRV 高于近期平均水平,代表身体恢复与整体状态较好。';
  420 +
  421 + @override
  422 + String get todayStressStatusInsufficientDataDescription =>
  423 + '当前可用数据不足,暂时无法准确判断压力状态。';
  424 +
  425 + @override
  426 + String get todayHrvMeasurementIntro =>
  427 + 'AppleWatch默认每2-5小时测量一次HRV,如果你希望立即手动进行测量,可以参考以下方法:';
  428 +
  429 + @override
  430 + String get todayHrvMeasurementStep1 => '1、戴紧AppleWatch,坐下来,保持心境平和';
  431 +
  432 + @override
  433 + String get todayHrvMeasurementStep2 => '2、打开AppleWatch上的「正念」里的「呼吸」';
  434 +
  435 + @override
  436 + String get todayHrvMeasurementStep3 => '3、保持平稳的呼吸,等待1-3分钟';
  437 +
  438 + @override
  439 + String get todayHrvMeasurementStep4 => '4、完成呼吸后,锁屏一次再解锁你的iPhone';
  440 +
  441 + @override
  442 + String get todayHrvMeasurementStep5 => '5、等待一分钟左右,StressWatch会收到你的数据并展示';
  443 +
  444 + @override
  445 + String get todayHrvMeasurementHint =>
  446 + '提示:数据源来自AppleWatch,在测量之后可能存在延迟或是数据未能同步的情况。如若出现上述情况,请重新测量并等待数据读取。';
  447 +
  448 + @override
  449 + String get todayHrvMeasurementWarning => '注意:需打开健康里的权限,同时关闭省电模式。';
  450 +
  451 + @override
  452 + String get todayStressStatusWhatTitle => '什么是综合压力状态?';
  453 +
  454 + @override
  455 + String get todayStressStatusWhatDescription1 =>
  456 + 'DoubleFeel 会结合你过去 30 天的 HRV(心率变异性)、静息心率以及当天的身体状态变化,综合评估你的整体压力水平。';
  457 +
  458 + @override
  459 + String get todayStressStatusWhatDescription2 =>
  460 + '由于 HRV 会随着情绪、运动、睡眠和疲劳不断波动,单次数据参考意义有限,因此我们更建议关注一整天的综合压力状态,让结果更稳定、更有参考价值。综合压力不仅能帮助你了解自己的身体状态,也能让亲密联系人更及时地关注你的变化。';
  461 +
  462 + @override
  463 + String get todayStressStatusWhyHrvTitle => '为什么要参考 HRV(心率变异性)?';
  464 +
  465 + @override
  466 + String get todayStressStatusWhyHrvDescription => 'HRV 是衡量身体压力与恢复能力的重要指标。';
  467 +
  468 + @override
  469 + String get todayStressStatusUsually => '通常来说:';
  470 +
  471 + @override
  472 + String get todayStressStatusHrvHigher => '· HRV 越高,代表身体恢复状态越好';
  473 +
  474 + @override
  475 + String get todayStressStatusHrvLower => '· HRV 越低,可能意味着疲劳、压力或睡眠不足';
  476 +
  477 + @override
  478 + String get todayStressStatusHrvChangesFast => '· HRV 变化较快,更适合观察短时间内的身体状态变化。';
  479 +
  480 + @override
  481 + String get todayStressStatusAppWatchDifferenceTitle =>
  482 + '手机 App 与 Apple Watch 显示的压力状态有什么区别?';
  483 +
  484 + @override
  485 + String get todayStressStatusAppWatchDifferenceApp =>
  486 + '手机 App 首页显示的是当天的综合压力状态,会综合分析 HRV、静息心率与整体趋势。';
  487 +
  488 + @override
  489 + String get todayStressStatusAppWatchDifferenceWatch =>
  490 + 'Apple Watch 显示的是最近一次的实时压力状态,更适合快速查看当前身体变化。';
  491 +
  492 + @override
  493 + String get todayStressStatusWaitingDataTitle => '为什么会出现“等待数据”?';
  494 +
  495 + @override
  496 + String get todayStressStatusWaitingDataDescription1 =>
  497 + '“等待数据”代表当前采集到的数据量不足,暂时无法生成可靠的压力评估。';
  498 +
  499 + @override
  500 + String get todayStressStatusWaitingDataDescription2 =>
  501 + '请继续佩戴 Apple Watch,等待系统自动采集数据。';
  502 +
  503 + @override
  504 + String get todayStressStatusWaitingDataReasonsIntro => '可能原因包括:';
  505 +
  506 + @override
  507 + String get todayStressStatusWaitingDataReason1 => '1. HRV 数据采样不足';
  508 +
  509 + @override
  510 + String get todayStressStatusWaitingDataReason2 => '2. 缺少静息心率数据';
  511 +
  512 + @override
  513 + String get todayStressStatusWaitingDataReason3 => '3. Apple Watch 佩戴时间较短';
  514 +
  515 + @override
  516 + String get todayStressStatusWaitingDataReason4 => '4. Apple Health 权限未开启';
  517 +
  518 + @override
  519 + String get todayHrvPrincipleHowMeasureTitle => 'DoubleFeel 如何测量压力状态?';
  520 +
  521 + @override
  522 + String get todayHrvPrincipleHowMeasureDescription1 =>
  523 + '当你正常佩戴 Apple Watch 时,系统会自动采集你的心率数据,并同步至 Apple Health。';
  524 +
  525 + @override
  526 + String get todayHrvPrincipleHowMeasureDescription2 =>
  527 + 'DoubleFeel 会基于这些数据计算 HRV(心率变异性)相关指标,用于评估你的身体压力与恢复状态。';
  528 +
  529 + @override
  530 + String get todayHrvPrincipleHowMeasureDescription3 =>
  531 + 'HRV 对压力、疲劳、睡眠、情绪与身体恢复都非常敏感,因此它能够帮助我们更早发现身体状态变化。';
  532 +
  533 + @override
  534 + String get todayHrvPrincipleHowMeasureDescription4 =>
  535 + '为了让结果更准确,DoubleFeel 会将你当前的 HRV 状态与过去 30 天的个人平均水平进行对比,而不是直接与其他人比较。';
  536 +
  537 + @override
  538 + String get todayRealtimeStressWhatTitle => '什么是实时压力?';
  539 +
  540 + @override
  541 + String get todayRealtimeStressWhatDescription1 =>
  542 + '实时压力是 DoubleFeel 根据你当前的 HRV、心率状态与个人历史数据变化,动态生成的身体压力指标。';
  543 +
  544 + @override
  545 + String get todayRealtimeStressWhatDescription2 =>
  546 + '压力值越高,代表你的身体状态相比平时偏离越明显,可能正处于疲劳、恢复不足或高压力状态。';
  547 +
  548 + @override
  549 + String get todayRealtimeStressWhatDescription3 =>
  550 + '它能够帮助你更快发现身体变化,并及时调整休息、运动与生活节奏。';
  551 +
  552 + @override
  553 + String get todayRealtimeStressDivisionTitle => '实时压力如何划分?';
  554 +
  555 + @override
  556 + String get todayRealtimeStressDivisionIntro => '实时压力以百分比形式展示:';
  557 +
  558 + @override
  559 + String get todayRealtimeStressExcellentRange => '状态优秀:1%~20%';
  560 +
  561 + @override
  562 + String get todayRealtimeStressNormalRange => '状态正常:21%~60%';
  563 +
  564 + @override
  565 + String get todayRealtimeStressCautionRange => '注意压力:61%~80%';
  566 +
  567 + @override
  568 + String get todayRealtimeStressOverloadRange => '压力过载:81%~100%';
  569 +
  570 + @override
  571 + String get todayRealtimeStressExcellentDescription => '身体恢复状态较好,整体较放松。';
  572 +
  573 + @override
  574 + String get todayRealtimeStressNormalDescription => '身体处于正常波动范围。';
  575 +
  576 + @override
  577 + String get todayRealtimeStressCautionDescription => '身体可能正在积累压力,需要适当休息与恢复。';
  578 +
  579 + @override
  580 + String get todayRealtimeStressOverloadDescription =>
  581 + '身体压力明显偏高,建议减少负荷、注意睡眠与恢复。';
  582 +
  583 + @override
  584 + String get todayRealtimeStressDivisionBaseline =>
  585 + '以上区间会结合你的个人基线动态调整,不同用户之间并不直接比较。';
  586 +
  587 + @override
  588 + String get todayRealtimeStressDivisionAwake => '此外,实时压力主要反映清醒状态下的身体压力变化。';
  589 +
  590 + @override
  591 + String get todayRealtimeStressLowBetterTitle => '实时压力越低越好吗?';
  592 +
  593 + @override
  594 + String get todayRealtimeStressLowBetterNo => '并不一定。';
  595 +
  596 + @override
  597 + String get todayRealtimeStressLowBetterType => '身体压力分为“正常压力”与“异常压力”。';
  598 +
  599 + @override
  600 + String get todayRealtimeStressLowBetterExample =>
  601 + '例如:运动期间或运动后,实时压力短时间升高属于正常恢复反应;工作专注、情绪兴奋时,压力也可能暂时升高,这些都属于正常的身体调节。';
  602 +
  603 + @override
  604 + String get todayRealtimeStressLowBetterHighStress =>
  605 + '但如果在静息、久坐或睡眠不足的情况下,压力长期偏高,则可能意味着身体疲劳、心理压力较大、睡眠恢复不足、运动恢复不充分、摄入过多咖啡因、酒精或刺激物、身体可能处于不适状态。';
  606 +
  607 + @override
  608 + String get todayRealtimeStressLowBetterTrend =>
  609 + 'DoubleFeel 更关注的是你的长期变化趋势,而不是单次波动。';
  610 +
  611 + @override
  612 + String get todayRealtimeStressScenarioTitle => 'HRV 与实时压力适用场景?';
  613 +
  614 + @override
  615 + String get todayRealtimeStressScenarioHrvDefault =>
  616 + '在 Apple Watch 的默认设置下,HRV 每 2~5 小时更新一次。';
  617 +
  618 + @override
  619 + String get todayRealtimeStressScenarioRegionLimit =>
  620 + '在部分地区,由于 Apple Watch 的呼吸功能受限,HRV 的更新频率可能会受到影响,并且开启呼吸功能后也会消耗更多电量。';
  621 +
  622 + @override
  623 + String get todayRealtimeStressScenarioIntro =>
  624 + '为了解决 HRV 更新间隔较长的问题,DoubleFeel 设计了实时压力功能:';
  625 +
  626 + @override
  627 + String get todayRealtimeStressScenarioUpdateEvery6Min => '· 实时压力每 6 分钟更新一次';
  628 +
  629 + @override
  630 + String get todayRealtimeStressScenarioTimely => '· 可以更及时地反映身体状态变化';
  631 +
  632 + @override
  633 + String get todayRealtimeStressScenarioConsistentTrend =>
  634 + '· 在大多数情况下,实时压力趋势与 HRV 趋势是一致的';
  635 +
  636 + @override
  637 + String get todayRealtimeStressScenarioSummary =>
  638 + '这样用户既能获得 HRV 的长期趋势,也能通过实时压力获得短时身体状态的参考。';
  639 +
  640 + @override
  641 + String get todayFaqNoDataTitle => 'APP或表盘有没有数据怎么办?';
  642 +
  643 + @override
  644 + String get todayFaqNoDataDescription1 =>
  645 + '1. 确认苹果手表系统在10.0以上,手机系统在14以上,系统版本可在「关于本机」内查看。';
  646 +
  647 + @override
  648 + String get todayFaqNoDataDescription2 =>
  649 + '2. 确认是否开启所有权限:手机「健康」-「共享」-「app」-「DoubleFeel」-「打开所有权限」。';
  650 +
  651 + @override
  652 + String get todayFaqNoDataDescription3 =>
  653 + '3. 确认设备是否处于省电模式、低电量状态或手表佩戴未贴紧,以上情况会影响手表数据采集。';
  654 +
  655 + @override
  656 + String get todayFaqContactPrefix => '如以上均检查无问题,可以';
  657 +
  658 + @override
  659 + String get todayFaqContactAction => '联系我们';
  660 +
  661 + @override
  662 + String get todayFaqContactSuffix => '。';
  663 +
  664 + @override
  665 + String get todayFaqWatchNoNotificationTitle => '手表无法收到状态通知?';
  666 +
  667 + @override
  668 + String get todayFaqWatchNoNotificationDescription1 =>
  669 + '苹果手表和手机的通知展示有优先级:当手机已解锁并亮屏时,通知只会在手机端展示,不会在手表上出现。';
  670 +
  671 + @override
  672 + String get todayFaqWatchNoNotificationDescription2 =>
  673 + '若压力数据可正常显示和自动更新,但手表未收到通知,可尝试以下操作:';
  674 +
  675 + @override
  676 + String get todayFaqWatchNoNotificationCheckPhoneNotification =>
  677 + '1. 检查手机是否打开通知(设置-DoubleFeel-通知)。';
  678 +
  679 + @override
  680 + String get todayFaqWatchNoNotificationCheckPhoneBackgroundRefresh =>
  681 + '2. 检查手机是否打开后台App刷新(设置-DoubleFeel-后台App刷新)。';
  682 +
  683 + @override
  684 + String get todayFaqWatchNoNotificationCheckWatchBackgroundRefresh =>
  685 + '3. 检查手表是否打开后台App刷新(设置-通用-后台App刷新,并确保DoubleFeel开启)。';
  686 +
  687 + @override
  688 + String get todayFaqWatchNoNotificationCheckModes =>
  689 + '4. 确保未处于低电量/专注/勿扰/剧院/睡眠等模式。';
  690 +
  691 + @override
  692 + String get todayFaqWatchNoNotificationReinstall =>
  693 + '5. 重装DoubleFeel 并重启AppleWatch与iPhone。';
  694 +
  695 + @override
  696 + String get todayFaqWatchFaceDelayTitle => '手表表盘数据不更新或有延迟?';
  697 +
  698 + @override
  699 + String get todayFaqWatchFaceDelayDescription1 =>
  700 + '由于苹果系统限制,所有手表表盘(第三方或官方)都会存在几分钟至半小时的延迟,开发者无法控制刷新频率。';
  701 +
  702 + @override
  703 + String get todayFaqWatchFaceDelayIfOverOneHour => '若手机数据刷新后超过1小时表盘仍未更新:';
  704 +
  705 + @override
  706 + String get todayFaqWatchFaceDelayOpenWatchApp =>
  707 + '请在手表上手动打开DoubleFeel,等待约1分钟。';
  708 +
  709 + @override
  710 + String get todayFaqWatchFaceDelayIfStill => '若仍未更新:';
  711 +
  712 + @override
  713 + String get todayFaqWatchFaceDelayRestartApp => '关闭DoubleFeel后台进程并重启。';
  714 +
  715 + @override
  716 + String get todayFaqWatchFaceDelayCheckIntro => '仍无效时,请检查:';
  717 +
  718 + @override
  719 + String get todayFaqWatchFaceDelayCheckData => '· 手机和手表app是否可正常看到HRV数据。';
  720 +
  721 + @override
  722 + String get todayFaqWatchFaceDelayCheckPhoneHealth =>
  723 + '· 确保手机端「iOS设置-隐私与安全性-健康-DoubleFeel」全部授权。';
  724 +
  725 + @override
  726 + String get todayFaqWatchFaceDelayCheckWatchHealth =>
  727 + '· 确保手表端「设置-健康-数据来源、App和服务-DoubleFeel」全部授权。';
  728 +
  729 + @override
  730 + String get todayFaqWatchFaceDelayCheckBackgroundRefresh =>
  731 + '· 确认AppleWatch-设置-通用-后台App刷新中DoubleFeel已开启。';
  732 +
  733 + @override
  734 + String get todayFaqWatchFaceDelayRestartWatch =>
  735 + '· 若仍未自动刷新,请重启手表。长时间运行或后台占用过高可能导致表盘暂停更新。';
  736 +
  737 + @override
  738 + String get todayFaqWatchFaceBlackScreenTitle => '手表表盘出现黑屏?';
  739 +
  740 + @override
  741 + String get todayFaqWatchFaceBlackScreenDescription =>
  742 + '若添加专属互动表盘后出现黑屏(仅显示时间和日期),可长按表盘,点击「编辑」,左滑至「复杂功能」,选择 DoubleFeel,然后按需选择各组件重新添加。';
350 } 743 }