Commit f82b284b382266c1a84ad83f652c5d8ed9382c68

Authored by 刘宏哲
1 parent f994f700

feat(app): update ui

... ... @@ -169,6 +169,8 @@ class FriendsController extends GetxController {
_friendListVersion != pendingOrder.listVersion) {
unawaited(refreshData());
}
ta.track('ita_doublefeel_friend_page',
properties: {'ita_content': '长按拖动卡片'});
} catch (error, stackTrace) {
AppLogger.e(
'FriendsController.reorderFriends failed',
... ... @@ -250,6 +252,7 @@ class FriendsController extends GetxController {
markName: selectedFriend.remark,
),
);
ta.track('success_set_doublefeel_friend_on_watch');
} catch (error, stackTrace) {
await loadFriends();
if (error is AppError) {
... ...
... ... @@ -66,6 +66,7 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> {
return _settingRow(
title: context.l10n.privacySettingsDisableAddById,
value: controller.disableAddWithUCode,
height: 56.dp,
onChanged: () => controller.isUpdatingDisableAddWithUCode.value
? null
: controller.updateDisableAddWithUCode,
... ... @@ -122,15 +123,11 @@ class PrivacySettingsView extends GetView<PrivacySettingsController> {
],
const Spacer(),
Obx(
() => Transform.scale(
scaleX: 52 / 51,
scaleY: 28 / 31,
child: CupertinoSwitch(
value: value.value,
activeTrackColor: AppColors.primary,
inactiveTrackColor: const Color(0xFFE5E5EA),
onChanged: onChanged(),
),
() => CupertinoSwitch(
value: value.value,
activeTrackColor: AppColors.primary,
inactiveTrackColor: const Color(0xFFE5E5EA),
onChanged: onChanged(),
),
),
],
... ...
import 'package:doublefeel_flutter/app/utils/font_utils.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import '../config/report_date_range_config.dart';
import '../models/report_period.dart';
... ... @@ -233,6 +234,8 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
late final FixedExtentScrollController _yearController;
late FixedExtentScrollController _monthController;
late FixedExtentScrollController _dayController;
int _monthControllerVersion = 0;
int _dayControllerVersion = 0;
@override
void initState() {
... ... @@ -271,6 +274,8 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
final lastMonth = _lastSelectableMonth;
final firstDay = _firstSelectableDay;
final lastDay = _lastSelectableDay;
final monthControllerVersion = _monthControllerVersion;
final dayControllerVersion = _dayControllerVersion;
final yearPicker = Expanded(
child: _picker(
controller: _yearController,
... ... @@ -290,6 +295,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
);
final monthPicker = Expanded(
child: _picker(
key: ValueKey('month-$_monthControllerVersion'),
controller: _monthController,
count: lastMonth - firstMonth + 1,
selectedIndex: _month - firstMonth,
... ... @@ -298,6 +304,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
Localizations.localeOf(context).toLanguageTag(),
),
onSelectedItemChanged: (index) {
if (monthControllerVersion != _monthControllerVersion) return;
setState(() {
_month = firstMonth + index;
_clampDay();
... ... @@ -307,6 +314,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
);
final dayPicker = Expanded(
child: _picker(
key: ValueKey('day-$_dayControllerVersion'),
controller: _dayController,
count: lastDay - firstDay + 1,
selectedIndex: _day - firstDay,
... ... @@ -315,6 +323,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
Localizations.localeOf(context).toLanguageTag(),
),
onSelectedItemChanged: (index) {
if (dayControllerVersion != _dayControllerVersion) return;
setState(() => _day = firstDay + index);
},
),
... ... @@ -365,6 +374,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
}
Widget _picker({
Key? key,
required FixedExtentScrollController controller,
required int count,
required int selectedIndex,
... ... @@ -372,6 +382,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
required ValueChanged<int> onSelectedItemChanged,
}) {
return CupertinoPicker.builder(
key: key,
scrollController: controller,
itemExtent: _itemExtent,
diameterRatio: 1.6,
... ... @@ -424,6 +435,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
final nextMonth = _month.clamp(firstMonth, lastMonth);
_month = nextMonth;
_monthController.dispose();
_monthControllerVersion++;
_monthController =
FixedExtentScrollController(initialItem: _month - firstMonth);
_clampDay();
... ... @@ -435,6 +447,7 @@ class _DayDatePickerSheetState extends State<_DayDatePickerSheet> {
final nextDay = _day.clamp(firstDay, lastDay);
_day = nextDay;
_dayController.dispose();
_dayControllerVersion++;
_dayController = FixedExtentScrollController(initialItem: _day - firstDay);
}
... ... @@ -757,7 +770,7 @@ class _AdjacentPicker extends StatelessWidget {
softWrap: false,
overflow: TextOverflow.visible,
style: TextStyle(
fontFamily: 'PingFangSC',
fontFamily: FontUtils.getFontFamily(context),
color: selected
? _ReportPickerColors.h1
: adjacent
... ...
import 'package:flutter/material.dart';
import '../../../utils/font_utils.dart';
class ReportDateSwitcher extends StatelessWidget {
const ReportDateSwitcher({
super.key,
... ... @@ -48,12 +50,12 @@ class ReportDateSwitcher extends StatelessWidget {
children: [
Text(
label,
style: const TextStyle(
style: TextStyle(
color: _brand,
fontSize: 14,
fontWeight: FontWeight.w500,
height: 1.2,
fontFamily: 'PingFangSC',
fontFamily: FontUtils.getFontFamily(context),
),
),
const SizedBox(width: 2),
... ...
import 'package:flutter/material.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:flutter/material.dart';
import 'sleep_report_text_styles.dart';
... ... @@ -68,8 +68,9 @@ class SleepQualityDialog extends StatelessWidget {
child: Text(
context.l10n.sleepQualityTitle,
textAlign: TextAlign.center,
strutStyle: SleepReportTextStyles.titleStrutStyle,
style: SleepReportTextStyles.titleTextStyle,
strutStyle:
SleepReportTextStyles.titleStrutStyle(context),
style: SleepReportTextStyles.titleTextStyle(context),
),
),
),
... ...
import 'package:flutter/foundation.dart';
import 'package:doublefeel_flutter/app/utils/font_utils.dart';
import 'package:flutter/material.dart';
abstract final class SleepReportTextStyles {
static const titleColor = Color(0xFF0F0F11);
static bool get _usesAppleTextRenderer =>
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS;
static TextStyle get titleTextStyle {
const baseStyle = TextStyle(
static TextStyle titleTextStyle(BuildContext context) {
final fontFamily = FontUtils.getFontFamily(context);
return TextStyle(
color: titleColor,
fontSize: 16,
fontWeight: FontWeight.w600,
height: 1.2,
);
if (!_usesAppleTextRenderer) return baseStyle;
return baseStyle.copyWith(
fontFamily: 'PingFangSC',
fontFamilyFallback: const ['Heiti SC', 'Arial Unicode MS'],
fontFamily: fontFamily,
);
}
static StrutStyle get titleStrutStyle {
if (!_usesAppleTextRenderer) {
return const StrutStyle(
fontSize: 16,
height: 1.2,
);
}
return const StrutStyle(
static StrutStyle titleStrutStyle(BuildContext context) {
final fontFamily = FontUtils.getFontFamily(context);
return StrutStyle(
fontSize: 16,
fontFamily: 'PingFangSC',
fontFamilyFallback: ['Heiti SC', 'Arial Unicode MS'],
height: 1.2,
fontFamily: fontFamily,
);
}
}
... ...
... ... @@ -34,10 +34,11 @@ class SleepWeekReportView extends StatelessWidget {
static const h3 = Color(0xFFB0B0B6);
static const grid = Color(0xFFF3F3F3);
static TextStyle get titleTextStyle => SleepReportTextStyles.titleTextStyle;
static TextStyle titleTextStyle(BuildContext context) =>
SleepReportTextStyles.titleTextStyle(context);
static StrutStyle get titleStrutStyle =>
SleepReportTextStyles.titleStrutStyle;
static StrutStyle titleStrutStyle(BuildContext context) =>
SleepReportTextStyles.titleStrutStyle(context);
static int minutesFromEvening(DateTime time) {
final minutes = time.hour * 60 + time.minute;
... ... @@ -463,8 +464,8 @@ class _TrendCard extends StatelessWidget {
children: [
Text(
title,
strutStyle: SleepWeekReportView.titleStrutStyle,
style: SleepWeekReportView.titleTextStyle,
strutStyle: SleepWeekReportView.titleStrutStyle(context),
style: SleepWeekReportView.titleTextStyle(context),
),
const SizedBox(height: 14),
Expanded(child: chart),
... ... @@ -616,6 +617,7 @@ class _DurationChartState extends State<_DurationChart> {
widget.compact,
),
barTouchData: _barTouchData(
context: context,
days: widget.days,
chartWidth: plotWidth,
compact: widget.compact,
... ... @@ -823,6 +825,7 @@ class _QualityChartState extends State<_QualityChart> {
widget.compact,
),
barTouchData: _barTouchData(
context: context,
days: widget.days,
chartWidth: plotWidth,
compact: widget.compact,
... ... @@ -1107,6 +1110,7 @@ class _BedtimeChartState extends State<_BedtimeChart> {
handleBuiltInTouches: false,
getTouchedSpotIndicator: _lineTouchedIndicators,
touchTooltipData: _lineTooltipData(
context: context,
days: widget.days,
dataBuilder: (report) => _ChartSummaryData.bedtime(
report,
... ... @@ -1292,6 +1296,7 @@ class _BedtimeAxis {
}
BarTouchData _barTouchData({
required BuildContext context,
required List<SleepReport> days,
required double chartWidth,
required bool compact,
... ... @@ -1350,7 +1355,7 @@ BarTouchData _barTouchData({
if (groupIndex < 0 || groupIndex >= days.length) return null;
final report = days[groupIndex];
if (!hasData(report)) return null;
return _barTooltipItem(dataBuilder(report));
return _barTooltipItem(context, dataBuilder(report));
},
),
);
... ... @@ -1390,10 +1395,12 @@ double _barCenterForIndex(
return chartLeft + barWidth / 2 + (barWidth + groupsSpace) * index;
}
BarTooltipItem _barTooltipItem(_ChartSummaryData data) {
BarTooltipItem _barTooltipItem(BuildContext context, _ChartSummaryData data) {
return BarTooltipItem(
data.title,
SleepWeekReportView.titleTextStyle.copyWith(color: data.titleColor),
SleepWeekReportView.titleTextStyle(context).copyWith(
color: data.titleColor,
),
textAlign: TextAlign.start,
children: [
TextSpan(
... ... @@ -1410,6 +1417,7 @@ BarTooltipItem _barTooltipItem(_ChartSummaryData data) {
}
LineTouchTooltipData _lineTooltipData({
required BuildContext context,
required List<SleepReport> days,
required _ChartSummaryData Function(SleepReport report) dataBuilder,
}) {
... ... @@ -1430,16 +1438,18 @@ LineTouchTooltipData _lineTooltipData({
if (!report.hasSleepData || report.heartRate.sleepStart == null) {
return null;
}
return _lineTooltipItem(dataBuilder(report));
return _lineTooltipItem(context, dataBuilder(report));
}).toList();
},
);
}
LineTooltipItem _lineTooltipItem(_ChartSummaryData data) {
LineTooltipItem _lineTooltipItem(BuildContext context, _ChartSummaryData data) {
return LineTooltipItem(
data.title,
SleepWeekReportView.titleTextStyle.copyWith(color: data.titleColor),
SleepWeekReportView.titleTextStyle(context).copyWith(
color: data.titleColor,
),
textAlign: TextAlign.start,
children: [
TextSpan(
... ...
import 'package:flutter/widgets.dart';
/// Provides locale-aware font selection for text widgets.
class FontUtils {
FontUtils._();
static const pingFangSc = 'PingFangSC';
/// Returns PingFang SC for Chinese and English; other languages use the
/// platform's default font.
static String? getFontFamily(BuildContext context) {
return getFontFamilyForLocale(Localizations.localeOf(context));
}
static String? getFontFamilyForLocale(Locale locale) {
switch (locale.languageCode.toLowerCase()) {
case 'zh':
case 'en':
return pingFangSc;
default:
return null;
}
}
}
... ...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'app_colors_extension.dart';
/// Class managing the Application ThemeData for both Light and Dark themes.
/// Automatically hooks up our custom Figma colors system as a ThemeExtension.
class AppTheme {
AppTheme._();
static String? get _platformFontFamily =>
defaultTargetPlatform == TargetPlatform.iOS ? 'PingFangSC' : null;
static const systemUiOverlayStyle = SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
... ... @@ -24,7 +22,6 @@ class AppTheme {
return ThemeData(
useMaterial3: true,
fontFamily: _platformFontFamily,
brightness: Brightness.light,
primaryColor: colors.primary,
scaffoldBackgroundColor: colors.backgroundPage,
... ... @@ -39,7 +36,6 @@ class AppTheme {
iconTheme: IconThemeData(color: colors.textPrimary),
actionsIconTheme: IconThemeData(color: colors.textPrimary),
titleTextStyle: TextStyle(
fontFamily: _platformFontFamily,
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w500,
... ... @@ -70,7 +66,6 @@ class AppTheme {
final colors = AppColorsExtension.dark();
return ThemeData(
useMaterial3: true,
fontFamily: _platformFontFamily,
brightness: Brightness.dark,
primaryColor: colors.primary,
scaffoldBackgroundColor: colors.backgroundLight,
... ... @@ -83,7 +78,6 @@ class AppTheme {
iconTheme: IconThemeData(color: colors.textPrimary),
actionsIconTheme: IconThemeData(color: colors.textPrimary),
titleTextStyle: TextStyle(
fontFamily: _platformFontFamily,
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w500,
... ... @@ -111,5 +105,6 @@ class AppTheme {
extension AppThemeContextExtension on BuildContext {
AppColorsExtension get colors =>
Theme.of(this).extension<AppColorsExtension>()!;
ThemeData get theme => Theme.of(this);
}
... ...