Commit 57589833e4087e094eef821ce65083033cca6139

Authored by 常守达
1 parent 76a7b3a9

fix(today):UI、loading修改

@@ -6,6 +6,7 @@ import 'package:doublefeel_flutter/app/modules/home/controllers/home_controller. @@ -6,6 +6,7 @@ import 'package:doublefeel_flutter/app/modules/home/controllers/home_controller.
6 import 'package:doublefeel_flutter/app/modules/home/controllers/trend/trend_controller.dart'; 6 import 'package:doublefeel_flutter/app/modules/home/controllers/trend/trend_controller.dart';
7 import 'package:doublefeel_flutter/app/modules/home/widgets/today/friend_select_bottom_sheet.dart'; 7 import 'package:doublefeel_flutter/app/modules/home/widgets/today/friend_select_bottom_sheet.dart';
8 import 'package:doublefeel_flutter/app/modules/home/widgets/today/no_health_data_page.dart'; 8 import 'package:doublefeel_flutter/app/modules/home/widgets/today/no_health_data_page.dart';
  9 +import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart';
9 import 'package:doublefeel_flutter/app/routes/app_pages.dart'; 10 import 'package:doublefeel_flutter/app/routes/app_pages.dart';
10 import 'package:doublefeel_flutter/core/constants/intent_keys.dart'; 11 import 'package:doublefeel_flutter/core/constants/intent_keys.dart';
11 import 'package:doublefeel_flutter/core/constants/membership.dart'; 12 import 'package:doublefeel_flutter/core/constants/membership.dart';
@@ -457,7 +458,8 @@ class TodayController extends GetMaterialController { @@ -457,7 +458,8 @@ class TodayController extends GetMaterialController {
457 arguments: getFriendTrendArguments(TrendType.hrv), 458 arguments: getFriendTrendArguments(TrendType.hrv),
458 ); 459 );
459 } else { 460 } else {
460 - Get.find<HomeController>().openTrend(TrendType.hrv); 461 + Get.find<HomeController>().openTrend(TrendType.hrv,
  462 + period: ReportPeriod.week, date: selectedDate.value);
461 } 463 }
462 } 464 }
463 465
@@ -468,7 +470,8 @@ class TodayController extends GetMaterialController { @@ -468,7 +470,8 @@ class TodayController extends GetMaterialController {
468 arguments: getFriendTrendArguments(TrendType.sleep), 470 arguments: getFriendTrendArguments(TrendType.sleep),
469 ); 471 );
470 } else { 472 } else {
471 - Get.find<HomeController>().openTrend(TrendType.sleep); 473 + Get.find<HomeController>().openTrend(TrendType.sleep,
  474 + period: ReportPeriod.day, date: selectedDate.value);
472 } 475 }
473 } 476 }
474 477
@@ -479,7 +482,8 @@ class TodayController extends GetMaterialController { @@ -479,7 +482,8 @@ class TodayController extends GetMaterialController {
479 arguments: getFriendTrendArguments(TrendType.activity), 482 arguments: getFriendTrendArguments(TrendType.activity),
480 ); 483 );
481 } else { 484 } else {
482 - Get.find<HomeController>().openTrend(TrendType.activity); 485 + Get.find<HomeController>().openTrend(TrendType.activity,
  486 + period: ReportPeriod.day, date: selectedDate.value);
483 } 487 }
484 } 488 }
485 489
@@ -8,6 +8,7 @@ import 'package:doublefeel_flutter/app/routes/app_pages.dart'; @@ -8,6 +8,7 @@ import 'package:doublefeel_flutter/app/routes/app_pages.dart';
8 import 'package:doublefeel_flutter/app/utils/dialog_utils.dart'; 8 import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
9 import 'package:doublefeel_flutter/core/config/app_environment_config.dart'; 9 import 'package:doublefeel_flutter/core/config/app_environment_config.dart';
10 import 'package:doublefeel_flutter/core/theme/app_theme.dart'; 10 import 'package:doublefeel_flutter/core/theme/app_theme.dart';
  11 +import 'package:doublefeel_flutter/core/util/size_extensions.dart';
11 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart'; 12 import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
12 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart'; 13 import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
13 import 'package:doublefeel_flutter/data/models/user/user_models.dart'; 14 import 'package:doublefeel_flutter/data/models/user/user_models.dart';
@@ -460,7 +461,11 @@ class _UnlockPremiumCard extends StatelessWidget { @@ -460,7 +461,11 @@ class _UnlockPremiumCard extends StatelessWidget {
460 ], 461 ],
461 ), 462 ),
462 ), 463 ),
463 - Image.asset('assets/images/my/bg_my_unlock_vip.png') 464 + Image.asset(
  465 + 'assets/images/my/bg_my_unlock_vip.png',
  466 + width: 149.w,
  467 + height: 128.w,
  468 + )
464 ], 469 ],
465 ), 470 ),
466 )); 471 ));
@@ -130,143 +130,135 @@ class TodayTabBody extends StatelessWidget { @@ -130,143 +130,135 @@ class TodayTabBody extends StatelessWidget {
130 parent: ClampingScrollPhysics(), 130 parent: ClampingScrollPhysics(),
131 ), 131 ),
132 slivers: [ 132 slivers: [
133 - SliverToBoxAdapter(  
134 - child: Container(  
135 - width: Get.width,  
136 - height: Get.width / 375 * (323 + pinnedHeaderHeight),  
137 - padding: EdgeInsets.only(bottom: 32),  
138 - child: Container(  
139 - // margin: EdgeInsets.only(top: pinnedHeaderHeight),  
140 - child: Column(  
141 - mainAxisAlignment: MainAxisAlignment.end,  
142 - children: [  
143 - Expanded(  
144 - child: Obx(() => Image.asset(  
145 - controller.v2StressScore.value?.stateBg() ??  
146 - 'assets/images/today/bg_stress_state_0.png',  
147 - fit: BoxFit.cover,  
148 - width: Get.width,  
149 - height: Get.width / 375 * 345,  
150 - )),  
151 - ),  
152 - Obx(  
153 - () => GestureDetector(  
154 - behavior: HitTestBehavior.opaque,  
155 - onTap: () {  
156 - showStressStatusExplanationBottomSheet(context);  
157 - },  
158 - child: Row(  
159 - mainAxisAlignment: MainAxisAlignment.center,  
160 - children: [  
161 - Text(  
162 - 'Hi,${controller.stressSubtitle.value}',  
163 - textAlign: TextAlign.center,  
164 - style: const TextStyle(  
165 - color: Color(0xFF78787D),  
166 - fontSize: 14,  
167 - fontWeight: FontWeight.w500,  
168 - ),  
169 - ),  
170 - const SizedBox(width: 4),  
171 - Image.asset(  
172 - 'assets/images/common/ic_info.png',  
173 - width: 14,  
174 - height: 14,  
175 - color: const Color(0xFF78787D),  
176 - ),  
177 - ], 133 + SliverList(
  134 + delegate: SliverChildListDelegate([
  135 + Obx(() => controller.isLoadingToday.value
  136 + ? Container(
  137 + width: Get.width,
  138 + height: Get.width / 375 * 345,
  139 + child: Align(
  140 + alignment: Alignment(0.0, 0.5),
  141 + child: CircularProgressIndicator()),
  142 + )
  143 + : Image.asset(
  144 + controller.v2StressScore.value!.stateBg(),
  145 + fit: BoxFit.cover,
  146 + width: Get.width,
  147 + height: Get.width / 375 * 345,
  148 + )),
  149 + Obx(
  150 + () => GestureDetector(
  151 + behavior: HitTestBehavior.opaque,
  152 + onTap: () {
  153 + showStressStatusExplanationBottomSheet(context);
  154 + },
  155 + child: Row(
  156 + mainAxisAlignment: MainAxisAlignment.center,
  157 + children: [
  158 + Text(
  159 + 'Hi,${controller.stressSubtitle.value}',
  160 + textAlign: TextAlign.center,
  161 + style: const TextStyle(
  162 + color: Color(0xFF78787D),
  163 + fontSize: 14,
  164 + fontWeight: FontWeight.w500,
178 ), 165 ),
179 ), 166 ),
180 - ),  
181 - Container(  
182 - height: 51,  
183 - alignment: Alignment.topCenter,  
184 - child: Obx(  
185 - () => controller.isLoadingToday.value  
186 - ? CircularProgressIndicator(strokeWidth: 2.5)  
187 - : Text(  
188 - controller.v2StressScore.value?.stateString() ??  
189 - '',  
190 - textAlign: TextAlign.center,  
191 - style: TextStyle(  
192 - color: context.colors.textPrimary,  
193 - fontSize: 28,  
194 - fontWeight: FontWeight.w500,  
195 - ),  
196 - ), 167 + const SizedBox(width: 4),
  168 + Image.asset(
  169 + 'assets/images/common/ic_info.png',
  170 + width: 14,
  171 + height: 14,
  172 + color: const Color(0xFF78787D),
197 ), 173 ),
198 - ), 174 + ],
  175 + ),
  176 + ),
  177 + ),
  178 + Obx(() => controller.isLoadingToday.value
  179 + ? SizedBox()
  180 + : Text(
  181 + controller.v2StressScore.value?.stateString() ?? '',
  182 + textAlign: TextAlign.center,
  183 + style: TextStyle(
  184 + color: context.colors.textPrimary,
  185 + fontSize: 28,
  186 + fontWeight: FontWeight.w500,
  187 + ),
  188 + )),
  189 + Center(
  190 + child: Obx(
  191 + () => StressProgressBar(
  192 + score: controller.v2StressScore.value?.state == 0
  193 + ? null
  194 + : controller.v2StressScore.value?.comprehensiveScore,
  195 + ),
  196 + ),
  197 + ),
  198 + SizedBox(
  199 + height: 36,
  200 + ),
  201 + Padding(
  202 + padding: const EdgeInsets.symmetric(horizontal: 16.0),
  203 + child: Column(
  204 + children: [
199 Obx( 205 Obx(
200 - () => StressProgressBar(  
201 - score: controller.v2StressScore.value?.state == 0  
202 - ? null  
203 - : controller.v2StressScore.value?.comprehensiveScore, 206 + () => controller.showHealthDataAuthCardStatus.value != 1
  207 + ? TodayHealthDataAuthCard(
  208 + onAuthorizeTap: () {
  209 + controller.onAuthorizeTap();
  210 + },
  211 + )
  212 + : const SizedBox.shrink(),
  213 + ),
  214 + Obx(() {
  215 + return Get.find<UserStateService>().isVip
  216 + ? const SizedBox.shrink()
  217 + : GestureDetector(
  218 + onTap: () {
  219 + ta.track('click_doublefeel_today_status',
  220 + properties: {'ita': '会员banner'});
  221 + controller.toPremiumDiscoutPage('今日-会员广告');
  222 + },
  223 + child: PremiumCard(
  224 + controller,
  225 + ));
  226 + }),
  227 + TodayAdCarousel(controller: controller),
  228 + TodayHrvNumberCard(controller: controller),
  229 + const SizedBox(height: 12),
  230 + TodayHrvChartCard(controller: controller),
  231 + const SizedBox(height: 12),
  232 + _LatestHrvCard(controller: controller),
  233 + if (!controller.isFriend) measureHRVButton(context),
  234 + Padding(
  235 + padding: const EdgeInsets.symmetric(vertical: 12),
  236 + child: Text(
  237 + (controller.isFriend
  238 + ? context.l10n.reportOtherPossessiveTitle(
  239 + context.l10n.dailyActions)
  240 + : context.l10n.dailyActions),
  241 + style: TextStyle(
  242 + color: context.colors.textPrimary,
  243 + fontSize: 16,
  244 + fontWeight: FontWeight.w600,
  245 + ),
204 ), 246 ),
205 ), 247 ),
  248 + TodaySleepCard(controller: controller),
  249 + TodayActivityCard(controller: controller),
206 ], 250 ],
207 ), 251 ),
208 ), 252 ),
209 - ),  
210 - ),  
211 - SliverPadding(  
212 - padding: const EdgeInsets.symmetric(horizontal: 16),  
213 - sliver: SliverList(  
214 - delegate: SliverChildListDelegate([  
215 - Obx(  
216 - () => controller.showHealthDataAuthCardStatus.value != 1  
217 - ? TodayHealthDataAuthCard(  
218 - onAuthorizeTap: () {  
219 - controller.onAuthorizeTap();  
220 - },  
221 - )  
222 - : const SizedBox.shrink(),  
223 - ),  
224 - Obx(() {  
225 - return Get.find<UserStateService>().isVip  
226 - ? const SizedBox.shrink()  
227 - : GestureDetector(  
228 - onTap: () {  
229 - ta.track('click_doublefeel_today_status',  
230 - properties: {'ita': '会员banner'});  
231 - controller.toPremiumDiscoutPage('今日-会员广告');  
232 - },  
233 - child: PremiumCard(  
234 - controller,  
235 - ));  
236 - }),  
237 - TodayAdCarousel(controller: controller),  
238 - TodayHrvNumberCard(controller: controller),  
239 - const SizedBox(height: 12),  
240 - TodayHrvChartCard(controller: controller),  
241 - const SizedBox(height: 12),  
242 - _LatestHrvCard(controller: controller),  
243 - if (!controller.isFriend) measureHRVButton(context),  
244 - Padding(  
245 - padding: const EdgeInsets.symmetric(vertical: 12),  
246 - child: Text(  
247 - (controller.isFriend  
248 - ? context.l10n  
249 - .reportOtherPossessiveTitle(context.l10n.dailyActions)  
250 - : context.l10n.dailyActions),  
251 - style: TextStyle(  
252 - color: context.colors.textPrimary,  
253 - fontSize: 16,  
254 - fontWeight: FontWeight.w600,  
255 - ),  
256 - ),  
257 - ),  
258 - TodaySleepCard(controller: controller),  
259 - TodayActivityCard(controller: controller),  
260 - Container(  
261 - alignment: Alignment.center,  
262 - margin: EdgeInsets.only(  
263 - bottom: 16,  
264 - // top: 28,  
265 - ),  
266 - child: ReportBottomSlogan(), 253 + Container(
  254 + alignment: Alignment.center,
  255 + margin: EdgeInsets.only(
  256 + bottom: 16,
  257 + // top: 28,
267 ), 258 ),
268 - ]),  
269 - ), 259 + child: ReportBottomSlogan(),
  260 + ),
  261 + ]),
270 ), 262 ),
271 ], 263 ],
272 ); 264 );
@@ -15,8 +15,6 @@ class DfTabBar extends StatelessWidget { @@ -15,8 +15,6 @@ class DfTabBar extends StatelessWidget {
15 static const double height = 56; 15 static const double height = 56;
16 static const double bottomMargin = 8; 16 static const double bottomMargin = 8;
17 17
18 - static const _selectedBgColor = Color(0xFFF3F3F3);  
19 -  
20 static const _tabIcons = [ 18 static const _tabIcons = [
21 'assets/images/tabbar/icon_today.png', 19 'assets/images/tabbar/icon_today.png',
22 'assets/images/tabbar/icon_trend.png', 20 'assets/images/tabbar/icon_trend.png',
@@ -73,10 +71,11 @@ class DfTabBar extends StatelessWidget { @@ -73,10 +71,11 @@ class DfTabBar extends StatelessWidget {
73 top: 0, 71 top: 0,
74 bottom: 0, 72 bottom: 0,
75 width: tabWidth, 73 width: tabWidth,
76 - child: const DecoratedBox( 74 + child: DecoratedBox(
77 decoration: BoxDecoration( 75 decoration: BoxDecoration(
78 - color: _selectedBgColor,  
79 - borderRadius: BorderRadius.all(Radius.circular(24)), 76 + color: context.colors.backgroundPage,
  77 + borderRadius:
  78 + const BorderRadius.all(Radius.circular(24)),
80 ), 79 ),
81 ), 80 ),
82 ), 81 ),
@@ -104,7 +103,7 @@ class DfTabBar extends StatelessWidget { @@ -104,7 +103,7 @@ class DfTabBar extends StatelessWidget {
104 tabLabels[i], 103 tabLabels[i],
105 style: TextStyle( 104 style: TextStyle(
106 fontSize: 9, 105 fontSize: 9,
107 - fontWeight: FontWeight.w500, 106 + fontWeight: FontWeight.w800,
108 color: isSelected 107 color: isSelected
109 ? context.colors.primary 108 ? context.colors.primary
110 : context.colors.textSecondary, 109 : context.colors.textSecondary,
@@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/data/models/pay/apple_pay_order_response.dart @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/data/models/pay/apple_pay_order_response.dart
9 import 'package:doublefeel_flutter/data/models/pay/pay_models.dart'; 9 import 'package:doublefeel_flutter/data/models/pay/pay_models.dart';
10 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart'; 10 import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
11 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart'; 11 import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
  12 +import 'package:flutter/material.dart';
12 import 'package:get/get.dart'; 13 import 'package:get/get.dart';
13 14
14 import '../../../routes/app_pages.dart'; 15 import '../../../routes/app_pages.dart';
@@ -24,6 +25,7 @@ class MembershipDetailController extends GetxController { @@ -24,6 +25,7 @@ class MembershipDetailController extends GetxController {
24 final PlatformHostApi _platformHostApi = PlatformHostApi(); 25 final PlatformHostApi _platformHostApi = PlatformHostApi();
25 26
26 final isRestoring = false.obs; 27 final isRestoring = false.obs;
  28 + final scrollController = ScrollController();
27 29
28 final isUnlocking = false.obs; 30 final isUnlocking = false.obs;
29 Future<void> unlock() async { 31 Future<void> unlock() async {
@@ -139,6 +141,12 @@ class MembershipDetailController extends GetxController { @@ -139,6 +141,12 @@ class MembershipDetailController extends GetxController {
139 _refreshVipInfo(); 141 _refreshVipInfo();
140 } 142 }
141 143
  144 + @override
  145 + void onClose() {
  146 + scrollController.dispose();
  147 + super.onClose();
  148 + }
  149 +
142 String? _nonEmpty(String? value) { 150 String? _nonEmpty(String? value) {
143 final trimmed = value?.trim(); 151 final trimmed = value?.trim();
144 if (trimmed == null || trimmed.isEmpty) return null; 152 if (trimmed == null || trimmed.isEmpty) return null;
@@ -17,43 +17,58 @@ class MembershipDetailView extends GetView<MembershipDetailController> { @@ -17,43 +17,58 @@ class MembershipDetailView extends GetView<MembershipDetailController> {
17 Widget build(BuildContext context) { 17 Widget build(BuildContext context) {
18 final userPrefs = Get.find<UserPreferencesStorage>(); 18 final userPrefs = Get.find<UserPreferencesStorage>();
19 return Scaffold( 19 return Scaffold(
20 - appBar: AppBar(  
21 - leading: IconButton(  
22 - highlightColor: Colors.transparent,  
23 - splashColor: Colors.transparent,  
24 - padding: EdgeInsets.zero,  
25 - onPressed: Get.back,  
26 - icon: Image.asset(  
27 - 'assets/images/common/ic_nav_back.webp',  
28 - width: 24,  
29 - height: 24,  
30 - ),  
31 - ),  
32 - actions: [  
33 - Padding(  
34 - padding: const EdgeInsets.only(right: 20.0),  
35 - child: TextButton(  
36 - onPressed: controller.isRestoring.value  
37 - ? null  
38 - : controller.restorePurchase,  
39 - style: TextButton.styleFrom(  
40 - foregroundColor: PurchaseColors.title,  
41 - padding: const EdgeInsets.symmetric(horizontal: 16),  
42 - textStyle: const TextStyle(fontSize: 14, height: 1.2), 20 + appBar: PreferredSize(
  21 + preferredSize: const Size.fromHeight(kToolbarHeight),
  22 + child: AnimatedBuilder(
  23 + animation: controller.scrollController,
  24 + builder: (context, _) {
  25 + final opacity = controller.scrollController.hasClients
  26 + ? (controller.scrollController.offset / 80).clamp(0.0, 1.0)
  27 + : 0.0;
  28 + return AppBar(
  29 + backgroundColor:
  30 + context.colors.backgroundPage.withOpacity(opacity),
  31 + surfaceTintColor: Colors.transparent,
  32 + leading: IconButton(
  33 + highlightColor: Colors.transparent,
  34 + splashColor: Colors.transparent,
  35 + padding: EdgeInsets.zero,
  36 + onPressed: Get.back,
  37 + icon: Image.asset(
  38 + 'assets/images/common/ic_nav_back.webp',
  39 + width: 24,
  40 + height: 24,
  41 + ),
43 ), 42 ),
44 - child: controller.isRestoring.value  
45 - ? const SizedBox(  
46 - width: 16,  
47 - height: 16,  
48 - child: CircularProgressIndicator(strokeWidth: 2),  
49 - )  
50 - : Text(context.l10n.purchaseRestore),  
51 - ),  
52 - ),  
53 - ], 43 + actions: [
  44 + Padding(
  45 + padding: const EdgeInsets.only(right: 20.0),
  46 + child: TextButton(
  47 + onPressed: controller.isRestoring.value
  48 + ? null
  49 + : controller.restorePurchase,
  50 + style: TextButton.styleFrom(
  51 + foregroundColor: PurchaseColors.title,
  52 + padding: const EdgeInsets.symmetric(horizontal: 16),
  53 + textStyle: const TextStyle(fontSize: 14, height: 1.2),
  54 + ),
  55 + child: controller.isRestoring.value
  56 + ? const SizedBox(
  57 + width: 16,
  58 + height: 16,
  59 + child: CircularProgressIndicator(strokeWidth: 2),
  60 + )
  61 + : Text(context.l10n.purchaseRestore),
  62 + ),
  63 + ),
  64 + ],
  65 + );
  66 + },
  67 + ),
54 ), 68 ),
55 extendBodyBehindAppBar: true, 69 extendBodyBehindAppBar: true,
56 body: SingleChildScrollView( 70 body: SingleChildScrollView(
  71 + controller: controller.scrollController,
57 physics: const ClampingScrollPhysics(), 72 physics: const ClampingScrollPhysics(),
58 padding: EdgeInsets.only( 73 padding: EdgeInsets.only(
59 bottom: MediaQuery.paddingOf(context).bottom + 16, 74 bottom: MediaQuery.paddingOf(context).bottom + 16,