From e672442d6a9e54adf2115314c8118452e2105cde Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Wed, 3 Dec 2025 11:49:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0UI=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config/app_config.dart | 8 +++ lib/pages/history_page.dart | 31 +-------- lib/pages/record_page.dart | 92 +------------------------- lib/utils/record_utils.dart | 23 +++++++ lib/widget/record/goal_progress.dart | 28 ++++---- lib/widget/record/record_calendar.dart | 40 +++++++++++ lib/widget/record/record_daily.dart | 92 +++++++++----------------- lib/widget/record/record_form.dart | 3 +- lib/widget/record/welcome.dart | 68 +++++++++++++++++++ 9 files changed, 196 insertions(+), 189 deletions(-) create mode 100644 lib/config/app_config.dart create mode 100644 lib/widget/record/record_calendar.dart create mode 100644 lib/widget/record/welcome.dart diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart new file mode 100644 index 0000000..a7e63cf --- /dev/null +++ b/lib/config/app_config.dart @@ -0,0 +1,8 @@ +class AppConfig { + static const int initWeight = 72; + static const int goalWeight = 70; + static const int goalTotalSpot = 600; + static const int goalAvgSleep = 8; + + static const List sportProjectList = ['羽毛球', '乒乓球', '跑步']; +} diff --git a/lib/pages/history_page.dart b/lib/pages/history_page.dart index 7365a80..df6c8a1 100644 --- a/lib/pages/history_page.dart +++ b/lib/pages/history_page.dart @@ -1,10 +1,8 @@ -import 'package:fitnote/provider/health_provider.dart'; import 'package:fitnote/service/health_service.dart'; +import 'package:fitnote/widget/record/record_calendar.dart'; import 'package:fitnote/widget/record/record_daily.dart'; import 'package:flutter/material.dart'; import 'package:flutter_common/widget/common_widget.dart'; -import 'package:table_calendar/table_calendar.dart'; -import 'package:provider/provider.dart'; class HistoryPage extends StatefulWidget { const HistoryPage({super.key}); @@ -16,36 +14,13 @@ class HistoryPage extends StatefulWidget { class _HistoryPageState extends State { final HealthService service = HealthService(); - TableCalendar _calendar() { - final provider = context.watch(); - - return TableCalendar( - locale: 'zh_CN', - headerStyle: const HeaderStyle( - headerPadding: EdgeInsets.symmetric(vertical: 0), - formatButtonVisible: false, - titleCentered: true, - ), - firstDay: DateTime.utc(2010, 1, 1), - lastDay: DateTime.utc(2100, 12, 31), - focusedDay: provider.selectedDate, - selectedDayPredicate: (day) => isSameDay(provider.selectedDate, day), - onDaySelected: (selectedDay, focusedDay) { - if (!isSameDay(provider.selectedDate, selectedDay)) { - provider.updateSelectDate(selectedDay); - provider.updateRecord(service.getRecordByDate(provider.selectedDate)); - } - } - ); - } - @override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( children: [ - CommonCard(child: _calendar()), - const SizedBox(height: 24), + CommonCard(child: RecordCalendar()), + const SizedBox(height: 10), RecordDaily(), ], ), diff --git a/lib/pages/record_page.dart b/lib/pages/record_page.dart index c9b58ba..4e21739 100644 --- a/lib/pages/record_page.dart +++ b/lib/pages/record_page.dart @@ -1,8 +1,6 @@ import 'package:fitnote/widget/record/goal_progress.dart'; import 'package:fitnote/widget/record/record_daily.dart'; import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:intl/intl.dart'; class RecordPage extends StatefulWidget { const RecordPage({super.key}); @@ -12,98 +10,14 @@ class RecordPage extends StatefulWidget { } class _RecordPageState extends State { - // 格式化当前日期 - String _formatCurrentDate() { - final now = DateTime.now(); - // 格式:2025年11月28日,周五 - return "${DateFormat('yyyy年MM月dd日').format(now)},${_getWeekday(now.weekday)}"; - } - - // 数字星期转中文 - String _getWeekday(int weekday) { - const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; - return weekdays[weekday - 1]; - } - @override Widget build(BuildContext context) { return SingleChildScrollView( child: Column( - children: [GoalProgress(), const SizedBox(height: 24), RecordDaily()], - ), - ); - } - - // 顶部导航栏 - Widget _buildHeader() { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), - color: Colors.white, - // boxShadow: const [ - // BoxShadow( - // color: Color(0x0A000000), - // blurRadius: 4, - // offset: Offset(0, 2), - // ), - // ], - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - '早上好,用户', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Color(0xFF1E293B), - ), - ), - const SizedBox(height: 4), - Text( - '今天是 ${_formatCurrentDate()}', - style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)), - ), - ], - ), - Row( - children: [ - // 通知按钮 - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: const Color(0xFFF1F5F9), - borderRadius: BorderRadius.circular(20), - ), - child: Icon( - FontAwesomeIcons.bell, - color: const Color(0xFF64748B), - size: 18, - ), - ), - // 用户头像 - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: const Color(0xFFE0F2FE), // 主色浅背景 - borderRadius: BorderRadius.circular(20), - ), - child: const Center( - child: Text( - 'U', - style: TextStyle( - color: Color(0xFF38BDF8), - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ), - ), - ], - ), + GoalProgress(), + const SizedBox(height: 10), + RecordDaily(), ], ), ); diff --git a/lib/utils/record_utils.dart b/lib/utils/record_utils.dart index d0c8648..0c03162 100644 --- a/lib/utils/record_utils.dart +++ b/lib/utils/record_utils.dart @@ -82,3 +82,26 @@ double formatProgress(double value) { return result; } } + +String formatContent(HealthRecord record, RecordType type) { + late String title; + switch (type) { + case RecordType.weight: + title = "${record.weight} kg"; + case RecordType.sport: + title = "${record.sportProject} · ${record.sportDuration} 分钟"; + case RecordType.sleep: + final startTime = record.sleepStartTime; + final endTime = record.sleepEndTime; + if (startTime.isNotEmpty && endTime.isNotEmpty) { + final duration = calSleepDuration(startTime, endTime); + title = "入睡 $startTime · 起床 $endTime \n${duration}"; + } else if (startTime.isNotEmpty) { + title = "入睡 $startTime"; + } else if (endTime.isNotEmpty) { + title = "起床 $endTime"; + } + } + + return title; +} diff --git a/lib/widget/record/goal_progress.dart b/lib/widget/record/goal_progress.dart index 361ead6..5c4fbcc 100644 --- a/lib/widget/record/goal_progress.dart +++ b/lib/widget/record/goal_progress.dart @@ -1,3 +1,4 @@ +import 'package:fitnote/config/app_config.dart'; import 'package:fitnote/models/health.dart'; import 'package:fitnote/provider/health_provider.dart'; import 'package:fitnote/service/health_service.dart'; @@ -19,11 +20,6 @@ class GoalProgress extends StatefulWidget { class _GoalProgressState extends State { final HealthService service = HealthService(); - final int initWeight = 72; - final int goalWeight = 70; - final int goalTotalSpot = 600; - final int goalAvgSleep = 8; - @override void initState() { super.initState(); @@ -55,27 +51,35 @@ class _GoalProgressState extends State { _buildProgressItem( title: '目标体重', current: provider.latestWeight.toString(), - target: goalWeight.toString(), + target: AppConfig.goalWeight.toString(), unit: 'kg', - progress: calWeightProgress(provider.latestWeight, goalWeight, initWeight), + progress: calWeightProgress( + provider.latestWeight, + AppConfig.goalWeight, + AppConfig.initWeight, + ), color: RecordType.weight.color, ), const SizedBox(height: 28), _buildProgressItem( title: '运动总时长', current: provider.totalSpot.toString(), - target: goalTotalSpot.toString(), + target: AppConfig.goalTotalSpot.toString(), unit: '分钟', - progress: formatProgress(provider.totalSpot / goalTotalSpot), + progress: formatProgress( + provider.totalSpot / AppConfig.goalTotalSpot, + ), color: RecordType.sport.color, ), const SizedBox(height: 28), _buildProgressItem( title: '日均睡眠', current: provider.avgSleep.toString(), - target: goalAvgSleep.toString(), + target: AppConfig.goalAvgSleep.toString(), unit: '小时', - progress: formatProgress(provider.avgSleep / goalAvgSleep), + progress: formatProgress( + provider.avgSleep / AppConfig.goalAvgSleep, + ), color: RecordType.sleep.color, ), ], @@ -111,7 +115,7 @@ class _GoalProgressState extends State { percentage: progress, progressBarColor: color, child: Text( - '${progress * 100}%', + '${roundNum(progress * 100)}%', textAlign: TextAlign.end, style: TextStyle(color: Colors.white), ), diff --git a/lib/widget/record/record_calendar.dart b/lib/widget/record/record_calendar.dart new file mode 100644 index 0000000..7fe0585 --- /dev/null +++ b/lib/widget/record/record_calendar.dart @@ -0,0 +1,40 @@ +import 'package:fitnote/provider/health_provider.dart'; +import 'package:fitnote/service/health_service.dart'; +import 'package:flutter/material.dart'; +import 'package:table_calendar/table_calendar.dart'; +import 'package:provider/provider.dart'; + +class RecordCalendar extends StatefulWidget { + const RecordCalendar({super.key}); + + @override + State createState() => _RecordCalendarState(); +} + +class _RecordCalendarState extends State { + final HealthService service = HealthService(); + + @override + Widget build(BuildContext context) { + final provider = context.watch(); + + return TableCalendar( + locale: 'zh_CN', + headerStyle: const HeaderStyle( + headerPadding: EdgeInsets.symmetric(vertical: 0), + formatButtonVisible: false, + titleCentered: true, + ), + firstDay: DateTime.utc(2010, 1, 1), + lastDay: DateTime.utc(2100, 12, 31), + focusedDay: provider.selectedDate, + selectedDayPredicate: (day) => isSameDay(provider.selectedDate, day), + onDaySelected: (selectedDay, focusedDay) { + if (!isSameDay(provider.selectedDate, selectedDay)) { + provider.updateSelectDate(selectedDay); + provider.updateRecord(service.getRecordByDate(provider.selectedDate)); + } + } + ); + } +} diff --git a/lib/widget/record/record_daily.dart b/lib/widget/record/record_daily.dart index 5327412..35c2a10 100644 --- a/lib/widget/record/record_daily.dart +++ b/lib/widget/record/record_daily.dart @@ -1,3 +1,4 @@ +import 'package:fitnote/config/app_config.dart'; import 'package:fitnote/models/health.dart'; import 'package:fitnote/provider/health_provider.dart'; import 'package:fitnote/service/health_service.dart'; @@ -21,7 +22,7 @@ class RecordDaily extends StatefulWidget { class _RecordDailyState extends State { final HealthService service = HealthService(); - + @override Widget build(BuildContext context) { return CommonCard( @@ -61,16 +62,12 @@ class _RecordDailyState extends State { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( - color: const Color(0xFFF1F5F9), + color: Colors.grey.shade100, borderRadius: BorderRadius.circular(16), ), child: Text( DateFormat('MM月dd日').format(provider.selectedDate), - style: const TextStyle( - fontSize: 12, - color: Color(0xFF64748B), - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 12), ), ); } @@ -108,14 +105,7 @@ class _RecordDailyState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text( - type.title, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: type.color, - ), - ), + _buildRecordTitle(type), const SizedBox(height: 4), _buildRecordContent(provider.currentRecord, type), ], @@ -174,7 +164,7 @@ class _RecordDailyState extends State { provider.updateWeight(70); break; case RecordType.sport: - provider.updateSportProject('跑步'); + provider.updateSportProject(AppConfig.sportProjectList[0]); provider.updateSportDuration(30); break; case RecordType.sleep: @@ -184,18 +174,18 @@ class _RecordDailyState extends State { } } - showDialog( - context: context, - builder: - (context) => Dialog( - child: Padding( - padding: EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [RecordForm(type: type), _buildDialogButton()], - ), - ), - ), + showDialog(context: context, builder: (context) => _buildDialog(type)); + } + + Widget _buildDialog(RecordType type) { + return Dialog( + child: Padding( + padding: EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [RecordForm(type: type), _buildDialogButton()], + ), + ), ); } @@ -210,7 +200,7 @@ class _RecordDailyState extends State { Navigator.of(context).pop(); _resetRecord(); }, - color: colors.secondary, + color: Colors.grey, text: "取消", ), ), @@ -250,7 +240,7 @@ class _RecordDailyState extends State { void _saveRecord() async { final provider = context.read(); - + final newRecord = HealthRecord( date: formatDate(provider.selectedDate), weight: provider.formItem.weight, @@ -261,48 +251,32 @@ class _RecordDailyState extends State { ); await service.updateRecord(newRecord); - provider.updateRecord(service.getRecordByDate(provider.selectedDate)); + _refreshGoalProcess(provider); + } + + void _refreshGoalProcess(HealthProvider provider) { provider.updateLatestWeight(service.getLatestWeight()); provider.updateTotalSpot(service.getTotalSport()); provider.updateAvgSleep(service.getAvgSleep()); } + Widget _buildRecordTitle(RecordType type) { + return Text( + type.title, + style: TextStyle(fontWeight: FontWeight.bold, color: type.color), + ); + } + Widget _buildRecordContent(HealthRecord record, RecordType type) { if (!checkHasRecordValue(record, type)) { return Text( "尚未记录今日数据,点击添加", - style: TextStyle(fontSize: 12, color: Colors.grey[500]), + style: TextStyle(fontSize: 14, color: Colors.grey[500]), ); } - late String title; - switch (type) { - case RecordType.weight: - title = "${record.weight} kg"; - case RecordType.sport: - title = "${record.sportProject} · ${record.sportDuration}分钟"; - case RecordType.sleep: - final startTime = record.sleepStartTime; - final endTime = record.sleepEndTime; - if (startTime.isNotEmpty && endTime.isNotEmpty) { - final duration = calSleepDuration(startTime, endTime); - title = "入睡 $startTime · 起床 $endTime · ${duration}"; - } else if (startTime.isNotEmpty) { - title = "入睡 $startTime"; - } else if (endTime.isNotEmpty) { - title = "起床 $endTime"; - } - } - - return Text( - title, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.grey[800], - ), - ); + return Text(formatContent(record, type), style: TextStyle(fontSize: 14)); } } diff --git a/lib/widget/record/record_form.dart b/lib/widget/record/record_form.dart index 52f9ca6..cc865a1 100644 --- a/lib/widget/record/record_form.dart +++ b/lib/widget/record/record_form.dart @@ -1,3 +1,4 @@ +import 'package:fitnote/config/app_config.dart'; import 'package:fitnote/models/health.dart'; import 'package:fitnote/provider/health_provider.dart'; import 'package:fitnote/service/health_service.dart'; @@ -109,7 +110,7 @@ class _RecordFormState extends State { SizedBox(width: 10), DropdownSelect( value: provider.formItem.sportProject, - items: ['羽毛球', '乒乓球', '篮球', '跑步'], + items: AppConfig.sportProjectList, onChanged: (value) => provider.updateSportProject(value!), ), ], diff --git a/lib/widget/record/welcome.dart b/lib/widget/record/welcome.dart new file mode 100644 index 0000000..c48cf57 --- /dev/null +++ b/lib/widget/record/welcome.dart @@ -0,0 +1,68 @@ +import 'package:fitnote/provider/health_provider.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_common/widget/common_widget.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +class Welcome extends StatefulWidget { + const Welcome({super.key}); + + @override + State createState() => _WelcomeState(); +} + +class _WelcomeState extends State { + @override + void initState() { + super.initState(); + } + + String _formatCurrentDate() { + final now = DateTime.now(); + return "${DateFormat('yyyy年MM月dd日').format(now)} ${_getWeekday(now.weekday)}"; + } + + // 数字星期转中文 + String _getWeekday(int weekday) { + const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + return weekdays[weekday - 1]; + } + + String getGreeting() { + int hour = DateTime.now().hour; + + if (hour >= 5 && hour < 12) { + return '早上好'; + } else if (hour >= 12 && hour < 18) { + return '下午好'; + } else { + return '晚上好'; + } + } + + @override + Widget build(BuildContext context) { + return CommonCard( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + getGreeting(), + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 4), + Text( + '今天是 ${_formatCurrentDate()}', + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + ], + ) + ], + ), + ); + } +}