feat:增加数据存储

This commit is contained in:
2025-12-01 23:01:23 +08:00
parent eb14cf1957
commit 2cc69b4019
16 changed files with 679 additions and 446 deletions

View File

@@ -1,4 +1,4 @@
import 'package:fitnote/modesl/menu.dart';
import 'package:fitnote/models/menu.dart';
import 'package:flutter/material.dart';
class AppNavbar extends StatefulWidget {

View File

@@ -1,12 +1,31 @@
import 'package:fitnote/models/health.dart';
import 'package:fitnote/pages/home_page.dart';
import 'package:fitnote/provider/health_provider.dart';
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const HealthLogApp());
void main() async{
// 初始化Hive
await Hive.initFlutter();
// 注册适配器
Hive.registerAdapter(HealthRecordAdapter());
final healthsBox = await Hive.openBox<HealthRecord>('healths');
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => HealthProvider())
],
child: MyApp(),
),
);
}
class HealthLogApp extends StatelessWidget {
const HealthLogApp({super.key});
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {

82
lib/models/health.dart Normal file
View File

@@ -0,0 +1,82 @@
import 'package:flutter/cupertino.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hive/hive.dart';
// flutter packages pub run build_runner build
part 'health.g.dart';
@HiveType(typeId: 0)
class HealthRecord extends HiveObject {
@HiveField(0)
int id;
@HiveField(1)
String sportProject;
@HiveField(2)
num sportDuration;
@HiveField(3)
num weight;
@HiveField(4)
String sleepStartTime;
@HiveField(5)
String sleepEndTime;
@HiveField(6)
num sleepDuration;
@HiveField(7)
String date;
HealthRecord({
required this.id,
required this.sportProject,
required this.sportDuration,
required this.weight,
required this.sleepStartTime,
required this.sleepEndTime,
required this.sleepDuration,
required this.date,
});
static HealthRecord getEmpty() {
return HealthRecord(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
sportProject: '',
sportDuration: 0,
weight: 0,
sleepStartTime: '',
sleepEndTime: '',
sleepDuration: 0,
date: '',
);
}
static HealthRecord getDefault() {
return HealthRecord(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
sportProject: '跑步',
sportDuration: 30,
weight: 70,
sleepStartTime: '',
sleepEndTime: '',
sleepDuration: 0,
date: '',
);
}
}
enum RecordType {
weight('体重', FontAwesomeIcons.weightScale, Color(0xFFF59E0B)),
sport('运动', FontAwesomeIcons.personRunning, Color(0xFF38BDF8)),
sleep('睡眠', FontAwesomeIcons.moon, Color(0xFF10B981));
final String title;
final IconData icon;
final Color color;
const RecordType(this.title, this.icon, this.color);
}

62
lib/models/health.g.dart Normal file
View File

@@ -0,0 +1,62 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'health.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class HealthRecordAdapter extends TypeAdapter<HealthRecord> {
@override
final int typeId = 0;
@override
HealthRecord read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return HealthRecord(
id: fields[0] as int,
sportProject: fields[1] as String,
sportDuration: fields[2] as num,
weight: fields[3] as num,
sleepStartTime: fields[4] as String,
sleepEndTime: fields[5] as String,
sleepDuration: fields[6] as num,
date: fields[7] as String,
);
}
@override
void write(BinaryWriter writer, HealthRecord obj) {
writer
..writeByte(8)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.sportProject)
..writeByte(2)
..write(obj.sportDuration)
..writeByte(3)
..write(obj.weight)
..writeByte(4)
..write(obj.sleepStartTime)
..writeByte(5)
..write(obj.sleepEndTime)
..writeByte(6)
..write(obj.sleepDuration)
..writeByte(7)
..write(obj.date);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is HealthRecordAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -1,57 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:json_annotation/json_annotation.dart';
@JsonSerializable()
class HealthRecord {
@JsonKey(name: 'id')
int? id;
@JsonKey(name: 'sportProject')
String? sportProject;
@JsonKey(name: 'sportDuration')
num? sportDuration;
@JsonKey(name: 'weight')
num? weight;
@JsonKey(name: 'sleepStartTime')
String? sleepStartTime;
@JsonKey(name: 'sleepEndTime')
String? sleepEndTime;
@JsonKey(name: 'sleepDuration')
num? sleepDuration;
@JsonKey(name: 'date')
String date;
HealthRecord({
this.id,
this.sportProject,
this.sportDuration,
this.weight,
this.sleepStartTime,
this.sleepEndTime,
this.sleepDuration,
required this.date,
});
// factory Health.fromJson(Map<String, dynamic> json) => _$HealthFromJson(json);
//
// Map<String, dynamic> toJson() => _$HealthToJson(this);
}
enum RecordType {
weight('体重', FontAwesomeIcons.weightScale, Color(0xFFF59E0B)),
sport('运动', FontAwesomeIcons.personRunning, Color(0xFF38BDF8)),
sleep('睡眠', FontAwesomeIcons.moon, Color(0xFF10B981));
final String title;
final IconData icon;
final Color color;
const RecordType(this.title, this.icon, this.color);
}

View File

@@ -1,5 +1,5 @@
import 'package:fitnote/layout/app_navbar.dart';
import 'package:fitnote/modesl/menu.dart';
import 'package:fitnote/models/menu.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {

View File

@@ -1,5 +1,5 @@
import 'package:fitnote/widget/record/goal_progress.dart';
import 'package:fitnote/widget/record/today_record.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';
@@ -24,12 +24,13 @@ class _RecordPageState extends State<RecordPage> {
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
return weekdays[weekday - 1];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [GoalProgress(), const SizedBox(height: 24), TodayRecord()],
children: [GoalProgress(), const SizedBox(height: 24), RecordDaily()],
),
);
}

View File

@@ -0,0 +1,43 @@
import 'package:fitnote/models/health.dart';
import 'package:flutter/material.dart';
class HealthProvider with ChangeNotifier {
late HealthRecord _formItem;
HealthRecord get formItem => _formItem;
void resetForm() {
_formItem = HealthRecord.getEmpty();
notifyListeners();
}
void initForm(HealthRecord record) {
_formItem = record;
notifyListeners();
}
void updateWeight(num weight) {
_formItem.weight = weight;
notifyListeners();
}
void updateSportProject(String sportProject) {
_formItem.sportProject = sportProject;
notifyListeners();
}
void updateSportDuration(num sportDuration) {
_formItem.sportDuration = sportDuration;
notifyListeners();
}
void updateSleepStartTime(String sleepStartTime) {
_formItem.sleepStartTime = sleepStartTime;
notifyListeners();
}
void updateSleepEndTime(String sleepEndTime) {
_formItem.sleepEndTime = sleepEndTime;
notifyListeners();
}
}

View File

@@ -0,0 +1,46 @@
import 'package:fitnote/models/health.dart';
import 'package:hive_flutter/hive_flutter.dart';
class HealthService {
static const String boxName = 'healths';
Box<HealthRecord> get box => Hive.box<HealthRecord>(boxName);
Future<bool> addRecord(HealthRecord record) async {
try {
await box.add(record);
return true;
} catch (e) {
return false;
}
}
List<HealthRecord> getAllRecords() {
return box.values.toList();
}
HealthRecord getAllRecordByDate(String date) {
return box.values.toList().firstWhere(
(item) => item.date == date,
orElse: () => HealthRecord.getEmpty(),
);
}
Future<bool> updateRecord(HealthRecord record) async {
try {
await record.save();
return true;
} catch (e) {
return false;
}
}
Future<bool> deleteRecord(HealthRecord record) async {
try {
await record.delete();
return true;
} catch (e) {
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
import 'package:fitnote/modesl/health.dart';
import 'package:fitnote/models/health.dart';
import 'package:fitnote/widget/record/common.dart';
import 'package:flutter/material.dart';
import 'package:flutter_common/widget/common_widget.dart';

View File

@@ -0,0 +1,211 @@
import 'package:fitnote/models/health.dart';
import 'package:fitnote/provider/health_provider.dart';
import 'package:fitnote/service/health_service.dart';
import 'package:fitnote/widget/record/common.dart';
import 'package:fitnote/widget/record/record_form.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 RecordDaily extends StatefulWidget {
const RecordDaily({super.key});
@override
State<RecordDaily> createState() => _RecordDailyState();
}
class _RecordDailyState extends State<RecordDaily> {
final HealthService service = HealthService();
late HealthRecord _record;
@override
void initState() {
super.initState();
_record = service.getAllRecordByDate('2025-12-01');
}
final recordForm = HealthRecord(
id: 1,
sportProject: '羽毛球',
sportDuration: 20,
sleepStartTime: '23:00',
sleepEndTime: '24:00',
sleepDuration: 60,
weight: 70,
date: '2025-11-28',
);
@override
Widget build(BuildContext context) {
return CommonCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
buildCardTitle(
context: context,
title: '今日记录',
icon: FontAwesomeIcons.calendarCheck,
),
_buildTodayTag(),
],
),
const SizedBox(height: 20),
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: RecordType.values.length,
separatorBuilder: (context, index) => SizedBox(height: 8),
itemBuilder: (context, index) {
final recordType = RecordType.values[index];
return _buildRecordListTile(recordType);
},
),
],
),
);
}
Widget _buildTodayTag() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(16),
),
child: Text(
DateFormat('MM月dd日').format(DateTime.now()),
style: const TextStyle(
fontSize: 12,
color: Color(0xFF64748B),
fontWeight: FontWeight.w500,
),
),
);
}
Widget _buildRecordListTile(RecordType type) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [type.color.withAlpha(10), Colors.white.withAlpha(200)],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: ListTile(
leading: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: type.color.withAlpha(30),
borderRadius: BorderRadius.circular(24),
),
child: Icon(type.icon, color: type.color, size: 22),
),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
type.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: type.color,
),
),
const SizedBox(height: 4),
_buildRecordContent(type),
],
),
trailing: GestureDetector(
onTap: () => onTapRecord(type),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: type.color.withAlpha(30),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
_checkHasRecordValue(type)
? FontAwesomeIcons.pencil
: FontAwesomeIcons.plus,
color: type.color,
size: 18,
),
),
),
splashColor: Colors.transparent,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
);
}
bool _checkHasRecordValue(RecordType type) {
switch (type) {
case RecordType.weight:
return _record.weight != 0;
case RecordType.sport:
return _record.sportProject != '';
case RecordType.sleep:
return _record.sleepStartTime != '';
}
}
void onTapRecord(RecordType type) {
final provider = context.read<HealthProvider>();
provider.initForm(HealthRecord.getDefault());
showDialog(
context: context,
builder: (context) => Dialog(child: RecordForm(type: type)),
);
}
Widget _buildRecordContent(RecordType type) {
if (!_checkHasRecordValue(type)) {
return Text(
"尚未记录今日数据,点击添加",
style: TextStyle(fontSize: 12, 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:
title =
"入睡 ${_record.sleepStartTime} · 起床 ${_record.sleepEndTime} · ${_record.sleepDuration}分钟";
}
return Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[800],
),
);
}
}

View File

@@ -0,0 +1,156 @@
import 'package:fitnote/models/health.dart';
import 'package:fitnote/provider/health_provider.dart';
import 'package:fitnote/service/health_service.dart';
import 'package:fitnote/widget/dropdown_select.dart';
import 'package:fitnote/widget/number_input.dart';
import 'package:flutter/material.dart';
import 'package:getwidget/getwidget.dart';
import 'package:provider/provider.dart';
class RecordForm extends StatefulWidget {
final RecordType type;
const RecordForm({super.key, required this.type});
@override
State<RecordForm> createState() => _RecordFormState();
}
class _RecordFormState extends State<RecordForm> {
final HealthService service = HealthService();
@override
Widget build(BuildContext context) {
return Padding(padding: EdgeInsets.all(20), child: _buildFormContent());
}
Widget _buildFormButton() {
final colors = Theme.of(context).colorScheme;
return Row(
children: [
Expanded(
child: GFButton(
onPressed: () => Navigator.of(context).pop(),
color: colors.secondary,
text: "取消",
),
),
const SizedBox(width: 12),
Expanded(
child: GFButton(
onPressed: () {
_saveRecord();
Navigator.of(context).pop();
},
color: colors.primary,
text: "确定",
),
),
],
);
}
void _saveRecord() {
final provider = context.read<HealthProvider>();
// service.updateRecord(provider.formItem);
}
Widget _buildFormTitle(String title) {
return Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
);
}
Widget _buildFiledTitle(String title) {
return Text(title, style: TextStyle(fontSize: 16));
}
Widget _buildFormContent() {
final provider = context.watch<HealthProvider>();
switch (widget.type) {
case RecordType.weight:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildFormTitle('记录体重'),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFiledTitle('体重:'),
SizedBox(width: 10),
NumberInput(
value: provider.formItem.weight,
min: 0,
max: 100,
step: 0.1,
unit: 'Kg',
onChanged: (value) => provider.updateWeight(value!),
),
],
),
SizedBox(height: 20),
_buildFormButton(),
],
);
case RecordType.sport:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildFormTitle('记录运动'),
SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFiledTitle('运动类型:'),
SizedBox(width: 10),
DropdownSelect(
value: provider.formItem.sportProject,
items: ['羽毛球', '乒乓球', '篮球', '跑步'],
onChanged: (value) => provider.updateSportProject(value!),
),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFiledTitle('运动时长:'),
SizedBox(width: 10),
NumberInput(
value: provider.formItem.sportDuration,
min: 5,
max: 240,
step: 5,
unit: '分钟',
onChanged: (value) => provider.updateSportDuration(value!),
),
],
),
SizedBox(height: 24),
_buildFormButton(),
],
);
case RecordType.sleep:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildFiledTitle('记录睡眠'),
SizedBox(height: 20),
// 这里添加睡眠相关的输入控件
SizedBox(height: 20),
_buildFormButton(),
],
);
}
}
}

View File

@@ -1,359 +0,0 @@
import 'package:fitnote/modesl/health.dart';
import 'package:fitnote/widget/dropdown_select.dart';
import 'package:fitnote/widget/number_input.dart';
import 'package:fitnote/widget/record/common.dart';
import 'package:fitnote/widget/select_input.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:getwidget/getwidget.dart';
import 'package:intl/intl.dart';
import 'package:numberpicker/numberpicker.dart';
class TodayRecord extends StatefulWidget {
const TodayRecord({super.key});
@override
State<TodayRecord> createState() => _TodayRecordState();
}
class _TodayRecordState extends State<TodayRecord> {
final record = HealthRecord(
id: 1,
sportProject: '羽毛球',
sportDuration: 20,
sleepStartTime: '23:00',
sleepEndTime: '24:00',
sleepDuration: 60,
weight: 0,
date: '2025-11-28',
);
final recordForm = HealthRecord(
id: 1,
sportProject: '羽毛球',
sportDuration: 20,
sleepStartTime: '23:00',
sleepEndTime: '24:00',
sleepDuration: 60,
weight: 70,
date: '2025-11-28',
);
@override
Widget build(BuildContext context) {
return CommonCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
buildCardTitle(
context: context,
title: '今日记录',
icon: FontAwesomeIcons.calendarCheck,
),
_buildTodayTag(),
],
),
const SizedBox(height: 20),
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: RecordType.values.length,
separatorBuilder: (context, index) => SizedBox(height: 8),
itemBuilder: (context, index) {
final recordType = RecordType.values[index];
return _buildRecordListTile(recordType);
},
),
],
),
);
}
Widget _buildTodayTag() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(16),
),
child: Text(
DateFormat('MM月dd日').format(DateTime.now()),
style: const TextStyle(
fontSize: 12,
color: Color(0xFF64748B),
fontWeight: FontWeight.w500,
),
),
);
}
Widget _buildRecordListTile(RecordType type) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [type.color.withAlpha(10), Colors.white.withAlpha(200)],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(10),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: ListTile(
leading: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: type.color.withAlpha(30),
borderRadius: BorderRadius.circular(24),
),
child: Icon(type.icon, color: type.color, size: 22),
),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
type.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: type.color,
),
),
const SizedBox(height: 4),
_buildRecordContent(type),
],
),
trailing: GestureDetector(
onTap: () => onTapRecord(type),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: type.color.withAlpha(30),
borderRadius: BorderRadius.circular(20),
),
child: Icon(
_checkHasRecordValue(type)
? FontAwesomeIcons.pencil
: FontAwesomeIcons.plus,
color: type.color,
size: 18,
),
),
),
splashColor: Colors.transparent,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
);
}
bool _checkHasRecordValue(RecordType type) {
switch (type) {
case RecordType.weight:
return record.weight != null;
case RecordType.sport:
return record.sportProject != null;
case RecordType.sleep:
return record.sleepStartTime != null;
}
}
Widget _buildDialogButton(RecordType type) {
final colors = Theme.of(context).colorScheme;
return Row(
children: [
Expanded(
child: GFButton(
onPressed: () => Navigator.of(context).pop(),
color: colors.secondary.withAlpha(100),
text: "取消",
),
),
const SizedBox(width: 12),
Expanded(
child: GFButton(onPressed: () {}, color: colors.primary, text: "确定"),
),
],
);
}
// void _saveRecordData(RecordType type) {
// setState(() {
// switch (type) {
// case RecordType.weight:
// record.weight = _dialogWeight;
// break;
// case RecordType.sport:
// record.sportProject = _dialogSportProject;
// record.sportDuration = _dialogSportDuration.toInt();
// break;
// case RecordType.sleep:
// // 处理睡眠数据
// break;
// }
// });
// }
Widget _buildDialogTitle(String title) {
return Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
);
}
Widget _buildFiledTitle(String title) {
return Text(title, style: TextStyle(fontSize: 16));
}
Widget _buildDialogContent(RecordType type) {
final colors = Theme.of(context).colorScheme;
switch (type) {
case RecordType.weight:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildDialogTitle('记录体重'),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFiledTitle('体重:'),
SizedBox(width: 10),
NumberInput(
value: recordForm.weight!,
min: 0,
max: 100,
step: 0.1,
unit: 'Kg',
onChanged:
(value) => setState(() => recordForm.weight = value!),
),
],
),
SizedBox(height: 20),
_buildDialogButton(type),
],
);
case RecordType.sport:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildDialogTitle('记录运动'),
SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFiledTitle('运动类型:'),
SizedBox(width: 10),
DropdownSelect(
value: recordForm.sportProject!,
items: ['羽毛球', '乒乓球', '篮球', '跑步'],
onChanged: (newValue) {
setState(() {
recordForm.sportProject = newValue!;
});
},
),
],
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 运动时长
_buildFiledTitle('运动时长:'),
SizedBox(width: 10),
NumberInput(
value: recordForm.sportDuration,
min: 5,
max: 240,
step: 5,
unit: '分钟',
onChanged:
(value) =>
setState(() => recordForm.sportDuration = value!),
),
],
),
SizedBox(height: 24),
_buildDialogButton(type),
],
);
case RecordType.sleep:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildFiledTitle('记录睡眠'),
SizedBox(height: 20),
// 这里添加睡眠相关的输入控件
SizedBox(height: 20),
_buildDialogButton(type),
],
);
}
}
void onTapRecord(RecordType type) {
showDialog(
context: context,
builder:
(context) => Dialog(
child: Padding(
padding: EdgeInsets.all(20),
child: _buildDialogContent(type),
),
),
);
}
Widget _buildRecordContent(RecordType type) {
if (!_checkHasRecordValue(type)) {
return Text(
"尚未记录今日数据,点击添加",
style: TextStyle(fontSize: 12, 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:
title =
"入睡 ${record.sleepStartTime} · 起床 ${record.sleepEndTime} · ${record.sleepDuration}分钟";
}
return Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.grey[800],
),
);
}
}