flutter_for_openharmony家庭药箱管理app实战+血糖记录实现
本文介绍了在Flutter for OpenHarmony应用中实现血糖记录功能的设计方案。系统采用卡片式布局,包含成员选择、趋势分析、最新记录和历史数据展示模块。血糖值根据不同测量时段(如空腹/餐后)采用差异化判断标准,并可视化显示健康状态。技术实现上使用Consumer2监听数据变化,通过单曲线图表展示血糖趋势,并添加渐变填充增强视觉效果。核心功能包括:1)时段标记与智能状态评估;2)7天数据

血糖监测对于糖尿病患者和高危人群来说至关重要。本文将介绍如何在Flutter for OpenHarmony应用中实现血糖记录功能,包括不同测量时段的记录、趋势分析、健康状态评估等实用功能。
设计思路
血糖记录与血压记录有相似之处,但也有其特殊性。血糖值会因测量时间不同而有不同的正常范围,比如空腹血糖和餐后血糖的标准就不一样。因此我们需要在记录时标注测量时段,并根据不同时段采用不同的判断标准。
页面采用简洁的卡片式设计,顶部是成员选择器,中间展示趋势图和最新记录,底部列出历史数据。血糖值使用大号字体居中显示,让用户一眼就能看到关键信息。
页面整体结构
页面使用Consumer2同时监听家庭成员和健康数据的变化:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('血糖记录')),
body: Consumer2<FamilyProvider, HealthProvider>(
builder: (context, familyProvider, healthProvider, child) {
if (_selectedMemberId == null && familyProvider.members.isNotEmpty) {
_selectedMemberId = familyProvider.members.first.id;
}
final records = _selectedMemberId != null
? healthProvider.getRecordsByMemberAndType(_selectedMemberId!, 'blood_sugar')
: <HealthRecord>[];
return SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildMemberSelector(familyProvider),
SizedBox(height: 16.h),
if (records.isNotEmpty) _buildChart(records),
SizedBox(height: 16.h),
_buildLatestRecord(records),
SizedBox(height: 16.h),
_buildRecordList(records),
],
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddDialog(context),
backgroundColor: const Color(0xFF00897B),
child: const Icon(Icons.add),
),
);
}
这里通过getRecordsByMemberAndType方法筛选出当前成员的血糖记录,类型参数传入'blood_sugar'。页面结构清晰,各个模块通过SizedBox进行间隔,视觉上更加舒适。
血糖趋势图表
血糖趋势图使用单条曲线展示,并添加了渐变填充效果:
Widget _buildChart(List<HealthRecord> records) {
final chartRecords = records.take(7).toList().reversed.toList();
return Container(
height: 200.h,
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2)),
],
),
child: LineChart(
LineChartData(
gridData: FlGridData(show: false),
titlesData: FlTitlesData(show: false),
borderData: FlBorderData(show: false),
lineBarsData: [
LineChartBarData(
spots: chartRecords.asMap().entries.map((e) {
return FlSpot(e.key.toDouble(), (e.value.data['value'] as double));
}).toList(),
isCurved: true,
color: Colors.blue,
barWidth: 2,
dotData: FlDotData(show: true),
belowBarData: BarAreaData(show: true, color: Colors.blue.withOpacity(0.1)),
),
],
),
),
);
}
与血压图表不同,血糖图表只有一条曲线,但增加了belowBarData配置,在曲线下方填充半透明的蓝色区域,让图表更有层次感。这种设计既美观又能突出数据趋势。
最新记录展示
最新记录卡片是页面的核心,需要根据测量时段判断血糖是否正常:
Widget _buildLatestRecord(List<HealthRecord> records) {
if (records.isEmpty) {
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(color: Colors.grey[100], borderRadius: BorderRadius.circular(12.r)),
child: Center(child: Text('暂无血糖记录', style: TextStyle(color: Colors.grey[500]))),
);
}
final latest = records.first;
final value = latest.data['value'] as double;
final measureTime = latest.data['measureTime'] as String;
String status = '正常';
Color statusColor = Colors.green;
if (measureTime == '空腹') {
if (value >= 7.0) {
status = '偏高';
statusColor = Colors.red;
} else if (value < 3.9) {
status = '偏低';
statusColor = Colors.orange;
}
} else {
if (value >= 11.1) {
status = '偏高';
statusColor = Colors.red;
}
}
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2)),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('最新记录', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
),
child: Text(status, style: TextStyle(color: statusColor, fontSize: 12.sp)),
),
],
),
SizedBox(height: 16.h),
Center(
child: Column(
children: [
Text(
value.toStringAsFixed(1),
style: TextStyle(fontSize: 48.sp, fontWeight: FontWeight.bold, color: Colors.blue),
),
Text('mmol/L', style: TextStyle(fontSize: 14.sp, color: Colors.grey[600])),
SizedBox(height: 8.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.r),
),
child: Text(measureTime, style: TextStyle(color: Colors.blue, fontSize: 12.sp)),
),
],
),
),
SizedBox(height: 8.h),
Text(
DateFormat('yyyy-MM-dd HH:mm').format(latest.recordDate),
style: TextStyle(fontSize: 12.sp, color: Colors.grey[500]),
),
],
),
);
}
这段代码实现了智能的血糖判断逻辑。空腹血糖正常范围是3.9-7.0 mmol/L,餐后血糖不应超过11.1 mmol/L。血糖值使用48号超大字体居中显示,配合单位和测量时段标签,让信息层次分明。
历史记录列表
历史记录以列表形式展示最近10条数据:
Widget _buildRecordList(List<HealthRecord> records) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('历史记录', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 8.h),
...records.take(10).map((record) {
return Container(
margin: EdgeInsets.only(bottom: 8.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8.r)),
child: Row(
children: [
Text(DateFormat('MM-dd').format(record.recordDate),
style: TextStyle(fontSize: 14.sp, color: Colors.grey[600])),
SizedBox(width: 16.w),
Text('${(record.data['value'] as double).toStringAsFixed(1)} mmol/L',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w500)),
const Spacer(),
Text(record.data['measureTime'] as String,
style: TextStyle(fontSize: 14.sp, color: Colors.grey[600])),
],
),
);
}).toList(),
],
);
}
每条记录横向排列,左侧显示日期,中间显示血糖值和单位,右侧显示测量时段。使用toStringAsFixed(1)保留一位小数,让数据显示更加规范。
添加记录对话框
添加血糖记录时需要选择测量时段,这里使用StatefulBuilder实现对话框内的状态管理:
void _showAddDialog(BuildContext context) {
final valueController = TextEditingController();
String measureTime = '空腹';
showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(
title: const Text('添加血糖记录'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: valueController,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(labelText: '血糖值 (mmol/L)'),
),
SizedBox(height: 16.h),
DropdownButtonFormField<String>(
value: measureTime,
decoration: const InputDecoration(labelText: '测量时间'),
items: ['空腹', '餐后1小时', '餐后2小时', '睡前'].map((t) {
return DropdownMenuItem(value: t, child: Text(t));
}).toList(),
onChanged: (v) => setDialogState(() => measureTime = v!),
),
],
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')),
TextButton(
onPressed: () {
if (_selectedMemberId != null) {
final member = context.read<FamilyProvider>().getMemberById(_selectedMemberId!);
if (member != null) {
final record = HealthRecord(
id: const Uuid().v4(),
memberId: member.id,
memberName: member.name,
recordDate: DateTime.now(),
type: 'blood_sugar',
data: {
'value': double.tryParse(valueController.text) ?? 0.0,
'measureTime': measureTime,
},
);
context.read<HealthProvider>().addRecord(record);
}
}
Navigator.pop(context);
Get.snackbar('成功', '血糖记录已添加', snackPosition: SnackPosition.BOTTOM);
},
child: const Text('保存'),
),
],
),
),
);
}
对话框包含血糖值输入框和测量时段下拉选择器。使用TextInputType.numberWithOptions(decimal: true)允许输入小数,因为血糖值通常带有小数点。StatefulBuilder让下拉选择器的变化能够实时反映在UI上。
成员选择器实现
成员选择器让用户可以切换查看不同家庭成员的血糖数据:
Widget _buildMemberSelector(FamilyProvider provider) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.grey[300]!),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _selectedMemberId,
isExpanded: true,
items: provider.members.map((m) {
return DropdownMenuItem(value: m.id, child: Text(m.name));
}).toList(),
onChanged: (value) => setState(() => _selectedMemberId = value),
),
),
);
}
选择器采用白色背景和浅灰色边框,与页面整体风格保持一致。当用户切换成员时,通过setState触发页面重建,自动加载新成员的血糖数据。
技术要点
分时段判断:血糖的正常范围因测量时段而异,代码中实现了空腹和餐后的不同判断标准,让健康评估更加准确。这种细致的判断逻辑体现了对医学知识的尊重。
小数处理:血糖值通常是小数,使用double类型存储,输入时允许小数点,显示时保留一位小数。这些细节处理让应用更加专业。
状态管理:对话框中使用StatefulBuilder实现局部状态管理,避免了创建新的StatefulWidget,代码更加简洁高效。
数据可视化:趋势图使用渐变填充效果,让图表更有视觉冲击力。最新记录使用超大字体居中显示,突出关键信息。
总结
血糖记录功能通过分时段记录和智能判断,为糖尿病患者提供了专业的健康管理工具。图表可视化让血糖变化趋势一目了然,帮助用户更好地控制血糖水平。这种数据驱动的健康管理方式,对慢性病管理具有重要意义。
血糖目标设置
支持设置个性化的血糖控制目标:
double _fastingTarget = 6.0;
double _postprandialTarget = 8.0;
Widget _buildTargetSettings() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2)),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('血糖目标', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 12.h),
_buildTargetRow('空腹血糖目标', _fastingTarget, (v) => setState(() => _fastingTarget = v)),
SizedBox(height: 8.h),
_buildTargetRow('餐后血糖目标', _postprandialTarget, (v) => setState(() => _postprandialTarget = v)),
],
),
);
}
Widget _buildTargetRow(String label, double value, ValueChanged<double> onChanged) {
return Row(
children: [
Expanded(child: Text(label, style: TextStyle(fontSize: 14.sp))),
Text('${value.toStringAsFixed(1)} mmol/L', style: TextStyle(fontSize: 14.sp, color: Colors.blue)),
IconButton(
icon: const Icon(Icons.edit, size: 16),
onPressed: () => _showTargetDialog(label, value, onChanged),
),
],
);
}
血糖目标设置让用户可以根据医生建议设置个性化的控制目标。空腹和餐后血糖有不同的目标值,系统会根据这些目标进行健康评估。
血糖统计分析
提供血糖数据的统计分析:
Widget _buildStatistics(List<HealthRecord> records) {
if (records.isEmpty) return const SizedBox.shrink();
final values = records.map((r) => r.data['value'] as double).toList();
final average = values.reduce((a, b) => a + b) / values.length;
final max = values.reduce((a, b) => a > b ? a : b);
final min = values.reduce((a, b) => a < b ? a : b);
final inRangeCount = records.where((r) {
final value = r.data['value'] as double;
final measureTime = r.data['measureTime'] as String;
if (measureTime == '空腹') {
return value >= 3.9 && value < 7.0;
} else {
return value < 11.1;
}
}).length;
final inRangeRate = inRangeCount / records.length * 100;
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2)),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('统计分析', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('平均值', '${average.toStringAsFixed(1)}', Colors.blue),
_buildStatItem('最高值', '${max.toStringAsFixed(1)}', Colors.red),
_buildStatItem('最低值', '${min.toStringAsFixed(1)}', Colors.green),
_buildStatItem('达标率', '${inRangeRate.toStringAsFixed(0)}%', Colors.orange),
],
),
],
),
);
}
统计分析显示平均值、最高值、最低值和达标率,帮助用户全面了解血糖控制情况。达标率是衡量血糖管理效果的重要指标。
饮食记录关联
支持记录血糖时关联饮食信息:
String? _mealNote;
Widget _buildMealInput() {
return TextField(
decoration: InputDecoration(
labelText: '饮食备注(可选)',
hintText: '记录本次测量前的饮食情况',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.r)),
),
onChanged: (v) => _mealNote = v,
);
}
饮食记录功能让用户可以记录测量前的饮食情况,帮助分析血糖波动的原因。这对于调整饮食习惯很有帮助。
异常值提醒
检测到异常血糖值时显示提醒:
Widget _buildAbnormalAlert(double value, String measureTime) {
String? alertMessage;
if (measureTime == '空腹') {
if (value < 3.9) {
alertMessage = '血糖偏低,请注意补充糖分,如有不适请及时就医';
} else if (value >= 7.0) {
alertMessage = '空腹血糖偏高,建议咨询医生调整治疗方案';
}
} else {
if (value >= 11.1) {
alertMessage = '餐后血糖偏高,建议控制饮食并咨询医生';
}
}
if (alertMessage == null) return const SizedBox.shrink();
return Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: Colors.red.withOpacity(0.3)),
),
child: Row(
children: [
Icon(Icons.warning, color: Colors.red, size: 20.sp),
SizedBox(width: 8.w),
Expanded(
child: Text(alertMessage, style: TextStyle(fontSize: 12.sp, color: Colors.red)),
),
],
),
);
}
异常值提醒功能在检测到血糖异常时显示警告信息,提醒用户注意并采取相应措施。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)