Commit bb436210b295bf09830aae0eb88f152e04c24aec

Authored by 刘宏哲
1 parent c14c1cfc

feat(app): update ui

... ... @@ -483,7 +483,7 @@ class _MonthEnergyTrendCardState extends State<_MonthEnergyTrendCard> {
const SizedBox(width: 2),
],
Text(
_comparisonText(comparisonPercent),
_comparisonText(context, comparisonPercent),
style: TextStyle(
color: ActivityBurnMonthReportView._h2,
fontSize: 12,
... ... @@ -577,13 +577,15 @@ class _MonthEnergyTrendCardState extends State<_MonthEnergyTrendCard> {
widget.report.days.any((day) => (day.activeEnergy?.value ?? 0) > 0) ||
widget.report.activeEnergyGoal > 0;
String _comparisonText(int? percent) {
String _comparisonText(BuildContext context, int? percent) {
if (percent == null || !widget.report.hasData) {
return '比上月-';
return context.l10n.activityComparedLastMonthUnavailable;
}
if (percent == 0) return '与上月持平';
final direction = percent > 0 ? '多' : '少';
return '比上月$direction${percent.abs()}%';
if (percent == 0) return context.l10n.activitySameAsLastMonth;
final absPercent = percent.abs();
return percent > 0
? context.l10n.activityMoreThanLastMonth(absPercent)
: context.l10n.activityLessThanLastMonth(absPercent);
}
double _barToY(int value, double maxY) {
... ...
... ... @@ -438,6 +438,7 @@ class _StressStatusRingPainter extends CustomPainter {
return;
}
final activeSegmentIndex = stressState.segmentIndex;
const paintOrder = [3, 2, 1, 0];
for (final i in paintOrder) {
final visualSpec = _arcSpecs[i].withGap(_arcGapDegrees);
... ... @@ -451,7 +452,9 @@ class _StressStatusRingPainter extends CustomPainter {
borderPaint,
);
segmentPaint.color = _segmentColors[i].withValues(alpha: 0.42);
segmentPaint.color = _segmentColors[i].withValues(
alpha: i == activeSegmentIndex ? 1 : 0.42,
);
canvas.drawArc(
rect,
startAngle,
... ... @@ -461,7 +464,7 @@ class _StressStatusRingPainter extends CustomPainter {
);
}
final segmentIndex = stressState.segmentIndex;
final segmentIndex = activeSegmentIndex;
if (segmentIndex == null) return;
final spec = _arcSpecs[segmentIndex].withGap(_arcGapDegrees);
final indicatorAngle =
... ...
... ... @@ -132,11 +132,19 @@ class HomeController extends GetxController {
return true;
}
void changeTab(int index) {
void changeTab(
int index, {
TrendEntrySource trendEntrySource = TrendEntrySource.bottomTab,
}) {
final previousIndex = selectedIndex.value;
selectedIndex.value = index;
if (index == trendTabIndex && previousIndex != trendTabIndex) {
Get.find<TrendController>().markPageVisible();
if (index == trendTabIndex) {
final trendController = Get.find<TrendController>();
if (previousIndex != trendTabIndex) {
trendController.markPageVisible(source: trendEntrySource);
} else {
trendController.updateEntrySource(trendEntrySource);
}
} else if (previousIndex == trendTabIndex && index != trendTabIndex) {
Get.find<TrendController>().markPageHidden();
}
... ... @@ -161,7 +169,10 @@ class HomeController extends GetxController {
if (fromTodayTab) {
Get.find<TrendController>().selectSelf();
}
changeTab(trendTabIndex);
changeTab(
trendTabIndex,
trendEntrySource: TrendEntrySource.jump,
);
}
int _dateKey(DateTime date) =>
... ... @@ -273,7 +284,10 @@ class HomeController extends GetxController {
if (trendType != null) {
openTrend(trendType, fromTodayTab: false);
} else {
changeTab(trendTabIndex);
changeTab(
trendTabIndex,
trendEntrySource: TrendEntrySource.jump,
);
}
break;
case 'friends':
... ...
... ... @@ -20,6 +20,11 @@ enum TrendType {
int get tabIndex => index;
}
enum TrendEntrySource {
bottomTab,
jump,
}
/// 趋势页顶层 Controller,仅负责:
/// 顶层 HRV / 活动 / 睡眠 类型切换 (selectedTypeIndex)
class TrendController extends GetxController with HealthTrendControl {
... ... @@ -28,6 +33,7 @@ class TrendController extends GetxController with HealthTrendControl {
final FriendApi _friendApi;
bool _isPageVisible = false;
final refreshToken = 0.obs;
final entrySource = TrendEntrySource.bottomTab.obs;
// 当前查看的用户。null 表示查看自己;非 null 表示查看指定用户。
final targetUserId = RxnInt();
... ... @@ -141,7 +147,17 @@ class TrendController extends GetxController with HealthTrendControl {
);
}
void markPageVisible() {
void updateEntrySource(TrendEntrySource source) {
entrySource.value = source;
}
void markPageVisible({
TrendEntrySource source = TrendEntrySource.bottomTab,
}) {
updateEntrySource(source);
if (source == TrendEntrySource.bottomTab) {
_resetQueryForBottomTabEntry();
}
if (_isPageVisible) return;
_isPageVisible = true;
refreshToken.value++;
... ... @@ -152,6 +168,11 @@ class TrendController extends GetxController with HealthTrendControl {
_isPageVisible = false;
}
void _resetQueryForBottomTabEntry() {
changeType(TrendType.hrv.tabIndex);
changeQuery(ReportPeriod.week, DateTime.now());
}
void _trackEnterPage() {
HealthTrendAnalytics.trackEnterPage(
selectedTypeIndex.value,
... ...
... ... @@ -36,7 +36,7 @@ class HomePage extends GetView<HomeController> {
bottomNavigationBar: Obx(
() => DfTabBar(
selectedIndex: controller.selectedIndex.value,
onTap: controller.changeTab,
onTap: (index) => controller.changeTab(index),
),
),
),
... ...
... ... @@ -77,6 +77,9 @@ class _HomeTrendHeader extends GetView<TrendController> {
avatarUrl: friendAvatar?.trim().isNotEmpty == true
? friendAvatar
: selfAvatar,
onTap: controller.canSwitchFriend
? controller.showFriendListBottomSheet
: null,
);
}),
Obx(
... ... @@ -104,14 +107,15 @@ class _HomeTrendHeader extends GetView<TrendController> {
}
class _TrendHeaderAvatar extends StatelessWidget {
const _TrendHeaderAvatar({this.avatarUrl});
const _TrendHeaderAvatar({this.avatarUrl, this.onTap});
final String? avatarUrl;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final imageUrl = avatarUrl?.trim();
return Container(
final avatar = Container(
width: 36,
height: 36,
decoration: ShapeDecoration(
... ... @@ -128,6 +132,18 @@ class _TrendHeaderAvatar extends StatelessWidget {
),
child: imageUrl?.isNotEmpty == true ? null : const _AvatarFallback(),
);
if (onTap == null) return avatar;
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: SizedBox(
width: 44,
height: 44,
child: Center(child: avatar),
),
);
}
}
... ...
... ... @@ -425,7 +425,7 @@ class _TrendMetric extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label,
style: const TextStyle(color: Color(0xFFB0B0B6), fontSize: 11)),
style: const TextStyle(color: Color(0xFF78787D), fontSize: 11)),
const SizedBox(height: 3),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
... ... @@ -497,7 +497,7 @@ class _TrendMetric extends StatelessWidget {
Color _comparisonColor(int? difference) {
if (difference == null || difference == 0) {
return const Color(0xFFB0B0B6);
return const Color(0xFF78787D);
}
return _isGoodChange(difference)
? const Color(0xFF3BD49D)
... ... @@ -675,7 +675,7 @@ class _DistributionCard extends StatelessWidget {
Text(
context.l10n.hrvValidDays,
style: const TextStyle(
color: Color(0xFFB0B0B6),
color: Color(0xFF78787D),
fontSize: 12,
),
),
... ... @@ -801,14 +801,14 @@ class _DistributionItem extends StatelessWidget {
style: TextStyle(
color: report.hasData
? const Color(0xFF0F0F11)
: const Color(0xFFB0B0B6),
: const Color(0xFF78787D),
fontSize: report.hasData ? 20 : 18,
fontWeight: FontWeight.w500,
height: 1.2)),
const SizedBox(height: 1),
Text('$percent%',
style: const TextStyle(
color: Color(0xFFB0B0B6),
color: Color(0xFF78787D),
fontSize: 12,
height: 1.2,
)),
... ... @@ -860,7 +860,7 @@ class _ExtremeHrv extends StatelessWidget {
const SizedBox(height: 2),
Text(
day == null ? '-月-日' : reportMonthDay(day!.date),
style: const TextStyle(color: Color(0xFFB0B0B6), fontSize: 12),
style: const TextStyle(color: Color(0xFF78787D), fontSize: 12),
),
],
);
... ...
... ... @@ -296,12 +296,12 @@ class _MonthExtreme extends StatelessWidget {
final separator =
Localizations.localeOf(context).languageCode == 'zh' ? ',' : ', ';
final value = months.isEmpty
? '-月'
? context.l10n.hrvEmptyMonthPlaceholder
: months.map((month) => reportMonth(month)).join(separator);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: const TextStyle(color: _h3, fontSize: 11)),
Text(label, style: const TextStyle(color: _h2, fontSize: 11)),
const SizedBox(height: 5),
Text(
value,
... ... @@ -317,13 +317,19 @@ class _StressAxisLabels extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const SizedBox(
return SizedBox(
width: 14,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RotatedBox(quarterTurns: 1, child: _StressAxisText('轻松')),
RotatedBox(quarterTurns: 1, child: _StressAxisText('压力大')),
RotatedBox(
quarterTurns: 1,
child: _StressAxisText(context.l10n.hrvRelaxedAxisLabel),
),
RotatedBox(
quarterTurns: 1,
child: _StressAxisText(context.l10n.hrvStressedAxisLabel),
),
],
),
);
... ... @@ -402,7 +408,7 @@ class _YearDistributionCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.l10n.hrvValidDays,
style: const TextStyle(color: _h3, fontSize: 11)),
style: const TextStyle(color: _h2, fontSize: 11)),
const SizedBox(height: 3),
_Number(
value: '${report.validDays}',
... ... @@ -601,12 +607,14 @@ class _DateExtreme extends StatelessWidget {
Localizations.localeOf(context).languageCode == 'zh' ? ',' : ', ';
final value = [
for (var i = 0; i < 2; i++)
i < days.length ? reportMonthDay(days[i].date) : '-月-日',
i < days.length
? reportMonthDay(days[i].date)
: context.l10n.hrvEmptyMonthDayPlaceholder,
].join(separator);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: const TextStyle(color: _h3, fontSize: 10)),
Text(label, style: const TextStyle(color: _h2, fontSize: 10)),
const SizedBox(height: 6),
Text(value,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
... ... @@ -644,10 +652,10 @@ class _LevelItem extends StatelessWidget {
? '$count${context.l10n.reportUnitDay}'
: context.l10n.reportWaitingForData,
style: TextStyle(
color: report.hasData ? _h1 : _h3,
color: report.hasData ? _h1 : _h2,
fontSize: report.hasData ? 19 : 13,
fontWeight: FontWeight.w500)),
Text('$percent%', style: const TextStyle(color: _h3, fontSize: 9)),
Text('$percent%', style: const TextStyle(color: _h2, fontSize: 9)),
],
);
}
... ...
import 'package:doublefeel_flutter/app/widget/premium_benefit_list_view.dart';
import 'package:doublefeel_flutter/core/util/size_extensions.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:doublefeel_flutter/r.dart';
import 'package:flutter/material.dart';
... ... @@ -43,7 +44,7 @@ class PurchaseView extends GetView<PurchaseController> {
children: [
const _HeroHeader(),
Padding(
padding: const EdgeInsets.only(left: 18, top: 9),
padding: EdgeInsets.only(left: 18, top: 9.h),
child: Text(
context.l10n.purchaseHeroTitle,
style: const TextStyle(
... ... @@ -99,7 +100,7 @@ class _HeroHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 320,
height: 320.h,
width: double.infinity,
child: Image.asset(
R.assetsImagesPurchaseTopBg,
... ...
... ... @@ -27,13 +27,15 @@ class SleepQualityDialog extends StatelessWidget {
Widget build(BuildContext context) {
return SafeArea(
top: false,
bottom: false,
child: Container(
height: 291,
decoration: const BoxDecoration(
color: _bg,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
padding: const EdgeInsets.only(bottom: 50),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 56,
... ... @@ -100,16 +102,6 @@ class SleepQualityDialog extends StatelessWidget {
],
),
),
const Spacer(),
Container(
width: 134,
height: 5,
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: SleepReportTextStyles.titleColor,
borderRadius: BorderRadius.circular(100),
),
),
],
),
),
... ...
... ... @@ -331,6 +331,10 @@
"hrvMoreStressed": "More stressed",
"hrvStressDistribution": "Stress Distribution",
"hrvDailyStressDistribution": "Daily Stress Distribution",
"hrvRelaxedAxisLabel": "Relaxed",
"hrvStressedAxisLabel": "Stressed",
"hrvEmptyMonthPlaceholder": "-",
"hrvEmptyMonthDayPlaceholder": "-",
"hrvValidDays": "Valid HRV days",
"hrvLowest": "Lowest HRV",
"hrvHighest": "Highest HRV",
... ... @@ -354,6 +358,10 @@
"activityComparedLastWeek": "34% less than last week",
"activityComparedLastMonth": "34% less than last month",
"activityComparedUnavailable": "Compared with last week: -",
"activityComparedLastMonthUnavailable": "Compared with last month: -",
"activitySameAsLastMonth": "Same as last month",
"activityMoreThanLastMonth": "{percent}% more than last month",
"activityLessThanLastMonth": "{percent}% less than last month",
"activityMove": "Move",
"activityExercise": "Exercise",
"activityStand": "Stand",
... ...
... ... @@ -503,6 +503,10 @@
"hrvMoreStressed": "压力较大",
"hrvStressDistribution": "压力分布",
"hrvDailyStressDistribution": "每日压力分布",
"hrvRelaxedAxisLabel": "轻松",
"hrvStressedAxisLabel": "压力大",
"hrvEmptyMonthPlaceholder": "-月",
"hrvEmptyMonthDayPlaceholder": "-月-日",
"hrvValidDays": "HRV有效天数",
"hrvLowest": "最低HRV",
"hrvHighest": "最高HRV",
... ... @@ -540,6 +544,24 @@
"activityComparedLastWeek": "比上周少34%",
"activityComparedLastMonth": "比上月少34%",
"activityComparedUnavailable": "比上周-",
"activityComparedLastMonthUnavailable": "比上月-",
"activitySameAsLastMonth": "与上月持平",
"activityMoreThanLastMonth": "比上月多{percent}%",
"@activityMoreThanLastMonth": {
"placeholders": {
"percent": {
"type": "int"
}
}
},
"activityLessThanLastMonth": "比上月少{percent}%",
"@activityLessThanLastMonth": {
"placeholders": {
"percent": {
"type": "int"
}
}
},
"activityMove": "活动",
"activityExercise": "锻炼",
"activityStand": "站立",
... ...
... ... @@ -2085,6 +2085,30 @@ abstract class AppLocalizations {
/// **'每日压力分布'**
String get hrvDailyStressDistribution;
/// No description provided for @hrvRelaxedAxisLabel.
///
/// In zh, this message translates to:
/// **'轻松'**
String get hrvRelaxedAxisLabel;
/// No description provided for @hrvStressedAxisLabel.
///
/// In zh, this message translates to:
/// **'压力大'**
String get hrvStressedAxisLabel;
/// No description provided for @hrvEmptyMonthPlaceholder.
///
/// In zh, this message translates to:
/// **'-月'**
String get hrvEmptyMonthPlaceholder;
/// No description provided for @hrvEmptyMonthDayPlaceholder.
///
/// In zh, this message translates to:
/// **'-月-日'**
String get hrvEmptyMonthDayPlaceholder;
/// No description provided for @hrvValidDays.
///
/// In zh, this message translates to:
... ... @@ -2223,6 +2247,30 @@ abstract class AppLocalizations {
/// **'比上周-'**
String get activityComparedUnavailable;
/// No description provided for @activityComparedLastMonthUnavailable.
///
/// In zh, this message translates to:
/// **'比上月-'**
String get activityComparedLastMonthUnavailable;
/// No description provided for @activitySameAsLastMonth.
///
/// In zh, this message translates to:
/// **'与上月持平'**
String get activitySameAsLastMonth;
/// No description provided for @activityMoreThanLastMonth.
///
/// In zh, this message translates to:
/// **'比上月多{percent}%'**
String activityMoreThanLastMonth(int percent);
/// No description provided for @activityLessThanLastMonth.
///
/// In zh, this message translates to:
/// **'比上月少{percent}%'**
String activityLessThanLastMonth(int percent);
/// No description provided for @activityMove.
///
/// In zh, this message translates to:
... ...
... ... @@ -1150,6 +1150,18 @@ class AppLocalizationsEn extends AppLocalizations {
String get hrvDailyStressDistribution => 'Daily Stress Distribution';
@override
String get hrvRelaxedAxisLabel => 'Relaxed';
@override
String get hrvStressedAxisLabel => 'Stressed';
@override
String get hrvEmptyMonthPlaceholder => '-';
@override
String get hrvEmptyMonthDayPlaceholder => '-';
@override
String get hrvValidDays => 'Valid HRV days';
@override
... ... @@ -1223,6 +1235,23 @@ class AppLocalizationsEn extends AppLocalizations {
String get activityComparedUnavailable => 'Compared with last week: -';
@override
String get activityComparedLastMonthUnavailable =>
'Compared with last month: -';
@override
String get activitySameAsLastMonth => 'Same as last month';
@override
String activityMoreThanLastMonth(int percent) {
return '$percent% more than last month';
}
@override
String activityLessThanLastMonth(int percent) {
return '$percent% less than last month';
}
@override
String get activityMove => 'Move';
@override
... ...
... ... @@ -1086,6 +1086,18 @@ class AppLocalizationsZh extends AppLocalizations {
String get hrvDailyStressDistribution => '每日压力分布';
@override
String get hrvRelaxedAxisLabel => '轻松';
@override
String get hrvStressedAxisLabel => '压力大';
@override
String get hrvEmptyMonthPlaceholder => '-月';
@override
String get hrvEmptyMonthDayPlaceholder => '-月-日';
@override
String get hrvValidDays => 'HRV有效天数';
@override
... ... @@ -1159,6 +1171,22 @@ class AppLocalizationsZh extends AppLocalizations {
String get activityComparedUnavailable => '比上周-';
@override
String get activityComparedLastMonthUnavailable => '比上月-';
@override
String get activitySameAsLastMonth => '与上月持平';
@override
String activityMoreThanLastMonth(int percent) {
return '比上月多$percent%';
}
@override
String activityLessThanLastMonth(int percent) {
return '比上月少$percent%';
}
@override
String get activityMove => '活动';
@override
... ...