Commit f692e51322e57a1cfea6ea3af73333a873758f5c

Authored by 常守达
1 parent eadccd2a

feat(today): 首页主题

... ... @@ -28,8 +28,10 @@ class HomeBinding extends Bindings {
),
fenix: true,
);
Get.lazyPut<MyController>(() => MyController(Get.find<UserApi>()),
fenix: true);
Get.lazyPut<MyController>(
() => MyController(Get.find<UserApi>(), Get.find<VipApi>()),
fenix: true,
);
Get.lazyPut<TrendController>(() => TrendController(), fenix: true);
Get.lazyPut<FriendsController>(() => FriendsController(), fenix: true);
}
... ...
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/core/network/api/user_api.dart';
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
class MyController extends GetxController {
MyController(this._userApi);
MyController(this._userApi, this._vipApi);
final UserApi _userApi;
final VipApi _vipApi;
final ImagePicker _picker = ImagePicker();
void updateNickname(String nickname) async {
... ... @@ -41,4 +44,15 @@ class MyController extends GetxController {
void testAppleHealthUpload() {
Get.toNamed(Routes.APPLE_HEALTH_UPLOAD_TEST);
}
Future<void> toPremiumPage() async {
await Get.toNamed(Routes.PURCHASE);
final vipResult = await _vipApi.getVipInfo();
if (vipResult case AppSuccess(data: final vip)) {
try {
final vipPrefs = UserPreferencesVipInfo.fromVipInfo(vip);
await Get.find<UserPreferencesStorage>().updateVipInfo(vipPrefs);
} on Exception catch (e) {}
}
}
}
... ...
import 'dart:async';
import 'dart:math';
import 'package:doublefeel_flutter/app/apple_health_upload/apple_health_upload_tool.dart';
import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_controller.dart';
... ... @@ -272,7 +273,13 @@ class TodayController extends GetxController {
switch (await _healthApi.getV2StressScore(friendUserId, intDate)) {
case AppSuccess(:final data):
v2StressScore.value = data;
// v2StressScore.value = data;
var nextInt = Random().nextInt(4);
v2StressScore.value = V2StressScore(
state: nextInt,
comprehensiveScore: 100 - (nextInt * 25),
);
case AppFailure():
v2StressScore.value = null;
}
... ...
... ... @@ -9,7 +9,6 @@ import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:doublefeel_flutter/core/util/size_extensions.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/friend/friend_models.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
... ... @@ -279,6 +278,7 @@ class _UnlockPremiumCard extends StatelessWidget {
behavior: HitTestBehavior.opaque,
onTap: () => Get.toNamed(Routes.PURCHASE),
child: Container(
height: 128,
padding: const EdgeInsets.fromLTRB(20, 14, 20, 14),
decoration: BoxDecoration(
gradient: const LinearGradient(
... ... @@ -291,41 +291,49 @@ class _UnlockPremiumCard extends StatelessWidget {
),
borderRadius: BorderRadius.circular(16),
),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: ShapeDecoration(
color: const Color(0xFFFFDF50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 32,
padding:
const EdgeInsets.symmetric(horizontal: 22, vertical: 8),
decoration: ShapeDecoration(
color: const Color(0xFFFFDF50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 4,
children: [
Image.asset('assets/images/common/ic_pro.png',
width: 18,
height: 18,
color: context.colors.textPrimary),
Text(
'立即解锁',
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
spacing: 4,
children: [
Image.asset('assets/images/common/ic_pro.png',
width: 18,
height: 18,
color: context.colors.textPrimary),
Text(
'立即解锁',
style: TextStyle(
color: context.colors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
],
),
),
],
),
));
}
... ...
import 'package:cached_network_image/cached_network_image.dart';
import 'package:doublefeel_flutter/app/modules/home/widgets/today/hrv_measurement_bottom_sheet.dart';
import 'package:doublefeel_flutter/app/modules/home/widgets/today/no_health_data_page.dart';
import 'package:doublefeel_flutter/app/modules/report_common/models/report_period.dart';
import 'package:doublefeel_flutter/app/modules/report_common/widgets/report_date_picker_sheet.dart';
... ... @@ -37,7 +36,6 @@ class TodayTabBody extends StatelessWidget {
final TodayController controller;
final _bgColor = const Color(0xFFF2F2F7);
final _weekCalendarHeight = 58.0;
final _topContentHeight = /*_topBarHeight + _weekCalendarHeight*/ 106.0;
final _weekRowsHeight = 50.0;
... ... @@ -49,7 +47,7 @@ class TodayTabBody extends StatelessWidget {
final pinnedHeaderHeight = topPadding + _topContentHeight;
return Container(
color: _bgColor,
color: context.colors.backgroundPage,
child: Stack(
children: [
NotificationListener<ScrollNotification>(
... ... @@ -73,9 +71,12 @@ class TodayTabBody extends StatelessWidget {
.clamp(0.0, 1.0)
.toDouble();
return Container(
color: const Color(0xFFDDD2FF).withValues(alpha: opacity),
);
return Obx(() => Container(
color:
(controller.v2StressScore.value?.getBackgroundColor() ??
Color(0xFFE3E1EC))
.withValues(alpha: opacity),
));
}),
),
Positioned(
... ... @@ -96,6 +97,9 @@ class TodayTabBody extends StatelessWidget {
controller.lastSelectableDay,
),
onBackToToday: _backToToday,
onTapPremiumCard: () {
controller.toPremiumPage();
},
),
_buildWeekCalendar(context),
],
... ... @@ -118,21 +122,38 @@ class TodayTabBody extends StatelessWidget {
child: Container(
height: 323 + pinnedHeaderHeight,
padding: EdgeInsets.only(bottom: 32),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFC5B0FF),
Color(0xFFF5F2FF),
],
),
),
// decoration: const BoxDecoration(
// gradient: LinearGradient(
// begin: Alignment.topCenter,
// end: Alignment.bottomCenter,
// colors: [
// Color(0xFFC5B0FF),
// Color(0xFFF5F2FF),
// ],
// ),
// ),
// decoration: BoxDecoration(
// image: DecorationImage(
// image: AssetImage(
// controller.v2StressScore.value?.stateBg() ??
// 'assets/images/today/bg_stress_state_0.png',
// ),
// fit: BoxFit.cover,
// alignment: Alignment.topCenter,
// ),
// ),
child: Container(
margin: EdgeInsets.only(top: pinnedHeaderHeight),
// margin: EdgeInsets.only(top: pinnedHeaderHeight),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: Obx(() => Image.asset(
controller.v2StressScore.value?.stateBg() ??
'assets/images/today/bg_stress_state_0.png',
fit: BoxFit.fitHeight,
)),
),
Obx(
() => GestureDetector(
behavior: HitTestBehavior.opaque,
... ... @@ -202,7 +223,9 @@ class TodayTabBody extends StatelessWidget {
Obx(() {
return Get.find<UserStateService>().isVip
? const SizedBox.shrink()
: const PremiumCard();
: GestureDetector(
onTap: () => controller.toPremiumPage(),
child: const PremiumCard());
}),
Obx(() => controller.showHrvAdBanner.value
? TodayHrvAdBanner(controller: controller)
... ... @@ -643,11 +666,13 @@ class _TopDateBar extends StatelessWidget {
required this.title,
required this.showBackToToday,
required this.onBackToToday,
required this.onTapPremiumCard,
});
final String title;
final bool showBackToToday;
final VoidCallback onBackToToday;
final VoidCallback onTapPremiumCard;
final _topBarHeight = 48.0;
@override
... ... @@ -699,35 +724,40 @@ class _TopDateBar extends StatelessWidget {
Obx(() {
return Get.find<UserStateService>().isVip
? const SizedBox.shrink()
: Container(
height: 29,
constraints: const BoxConstraints(minWidth: 95),
padding: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
color: const Color(0xFFFFDF51),
borderRadius: BorderRadius.circular(27.6),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'assets/images/common/ic_pro.png',
width: 20,
height: 20,
color: context.colors.textPrimary,
),
const SizedBox(width: 2),
Text(
'20%优惠',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
: GestureDetector(
onTap: () {
onTapPremiumCard.call();
},
child: Container(
height: 29,
constraints: const BoxConstraints(minWidth: 95),
padding: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
color: const Color(0xFFFFDF51),
borderRadius: BorderRadius.circular(27.6),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'assets/images/common/ic_pro.png',
width: 20,
height: 20,
color: context.colors.textPrimary,
),
),
],
const SizedBox(width: 2),
Text(
'20%优惠',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: context.colors.textPrimary,
),
),
],
),
),
);
}),
... ...
import 'package:doublefeel_flutter/app/actions/dialog_action.dart';
import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart';
import 'package:doublefeel_flutter/app/modules/home/controllers/my_controller.dart';
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
import 'package:doublefeel_flutter/core/network/api/user_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
... ... @@ -10,7 +13,6 @@ import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:doublefeel_flutter/r.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
... ... @@ -160,6 +162,13 @@ class AccountSettingView extends GetView<MyController> {
}
Future<void> _logout() async {
final confirmResult = await DialogUtils.showCommonDialog(DialogMetaData(
title: '退出登录',
message: '确认退出登录吗?',
confirmText: '确认退出',
iconAsset: null));
if (confirmResult.action != DialogAction.confirm) return;
await Get.find<UserStateService>().onLogout();
AppToast.show('已退出登录');
Get.offAllNamed(AppRoutes.login);
... ...
... ... @@ -38,6 +38,7 @@ class TodayHrvAdBanner extends StatelessWidget {
),
),
),
leftImagePath: 'assets/images/today/ic_banner_watch_hrv_theme.png',
onClose: controller.dismissHrvAdBanner,
);
});
... ... @@ -86,6 +87,7 @@ class TodayPartnerAdBanner extends StatelessWidget {
),
),
),
leftImagePath: 'assets/images/today/ic_banner_add_contact.png',
onClose: controller.dismissPartnerAdBanner,
);
});
... ... @@ -98,12 +100,14 @@ class _TodayGuideBanner extends StatelessWidget {
required this.subtitle,
required this.onClose,
this.right,
required this.leftImagePath,
});
final String title;
final String subtitle;
final VoidCallback onClose;
final Widget? right;
final String leftImagePath;
@override
Widget build(BuildContext context) {
... ... @@ -122,22 +126,10 @@ class _TodayGuideBanner extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
Image.asset(
leftImagePath,
width: 100,
height: double.infinity,
child: ColoredBox(
color: colors.chartPink.withValues(alpha: 0.72),
child: Center(
child: Text(
'插图',
style: TextStyle(
fontSize: 12,
color: colors.chartPink,
height: 1.2,
),
),
),
),
height: 65,
),
const SizedBox(width: 12),
Expanded(
... ...
... ... @@ -569,7 +569,7 @@ class TodayHrvChartCard extends StatelessWidget {
),
),
Positioned(
top: 97,
top: 57,
left: 0,
right: 0,
child: Column(
... ...
... ... @@ -176,6 +176,7 @@ class LoginController extends GetxController {
if (loginRes is AppSuccess<LoginResponse>) {
await _handleLoginSuccess(loginRes.data);
await Get.find<LocalStorage>().setLastLoginMethod('phone');
} else {
isLoggingIn.value = false;
}
... ... @@ -187,6 +188,7 @@ class LoginController extends GetxController {
if (loginRes is AppSuccess<LoginResponse>) {
await _handleLoginSuccess(loginRes.data, appleModel: appleModel);
await Get.find<LocalStorage>().setLastLoginMethod('apple');
} else {
isLoggingIn.value = false;
}
... ...
... ... @@ -143,7 +143,7 @@ class LoginView extends GetView<LoginController> {
right: rightOffset - 4,
bottom: Get.find<LocalStorage>().lastLoginMethod == 'apple'
? bottom(117)
: bottom(200),
: bottom(188),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: ShapeDecoration(
... ...
import 'dart:ui';
import 'package:doublefeel_flutter/core/theme/app_theme.dart';
import 'package:flutter/src/widgets/framework.dart';
... ... @@ -74,7 +76,7 @@ class V2SleepTimeRange {
const V2SleepTimeRange({required this.fromTime, required this.toTime});
final int fromTime; // Unix 时间戳(秒)
final int toTime; // Unix 时间戳(秒)
final int toTime; // Unix 时间戳(秒)
factory V2SleepTimeRange.fromJson(Map<String, dynamic> json) {
return V2SleepTimeRange(
... ... @@ -246,6 +248,34 @@ class V2StressScore {
}
return '等待数据';
}
String stateBg() {
switch (state) {
case 1:
return 'assets/images/today/bg_stress_state_1.png';
case 2:
return 'assets/images/today/bg_stress_state_2.png';
case 3:
return 'assets/images/today/bg_stress_state_3.png';
case 4:
return 'assets/images/today/bg_stress_state_4.png';
}
return 'assets/images/today/bg_stress_state_0.png';
}
Color getBackgroundColor() {
switch (state) {
case 1:
return const Color(0xFFFFE3EE);
case 2:
return const Color(0xFFFFE8DE);
case 3:
return const Color(0xFFD4E2FF);
case 4:
return const Color(0xFFC8F6E5);
}
return const Color(0xFFE3E1EC);
}
}
/// Single item in v2/hrv_trend/ list
... ...