1. 项目背景与核心需求
在家庭健康管理场景中,血压监测是基础但至关重要的功能模块。根据世界卫生组织统计,全球约有14亿高血压患者,其中近半数对自己的病情并不知情。传统纸质记录方式存在易丢失、难追溯、无法直观呈现趋势等问题。这正是我们开发家庭药箱管理App中血压记录模块的初衷。
这个模块需要解决三个核心痛点:
- 多成员数据隔离:一个家庭中可能有老人、成年人等不同成员需要监测血压
- 医学标准可视化:需要将枯燥的血压数值转化为直观的健康状态评估
- 长期趋势追踪:通过时间维度分析血压变化,及时发现异常波动
技术选型上,我们采用Flutter for OpenHarmony方案,主要基于:
- 跨平台一致性:一套代码可同时运行在OpenHarmony和Android/iOS设备上
- 性能表现:Flutter的Skia渲染引擎能保证血压图表的高流畅度
- 开发效率:Hot Reload特性极大缩短UI调试时间
2. 架构设计与技术方案
2.1 整体架构分层
采用典型的分层架构设计:
code复制表示层(UI)
├── 血压记录主页
├── 数据录入弹窗
└── 图表展示组件
业务逻辑层
├── 健康状态评估引擎
├── 数据过滤处理器
└── 时间序列分析
数据层
├── Hive本地数据库
├── 健康数据Model
└── 家庭成员数据同步
2.2 关键数据结构设计
血压记录的核心数据结构如下(Dart实现):
dart复制class HealthRecord {
final String id;
final String memberId;
final String memberName;
final DateTime recordDate;
final String type; // 'blood_pressure'
final Map<String, dynamic> data; // 包含systolic/diastolic/heartRate
// 构造函数及toJson/fromJson方法...
}
数据存储选用Hive而非SQLite的原因:
- 对Dart原生支持更好,无需ORM转换
- 在移动设备上读写性能更优
- 支持强类型数据模型
3. 核心功能实现细节
3.1 动态图表渲染方案
采用fl_chart库实现血压趋势图,关键配置参数:
dart复制LineChartData(
lineTouchData: LineTouchData(enabled: true), // 启用触摸交互
minX: 0,
maxX: 6, // 显示7天数据
lineBarsData: [
LineChartBarData(
spots: _generateSpots(records, 'systolic'),
isCurved: true, // 曲线平滑
curveSmoothness: 0.35,
color: Colors.red[400],
barWidth: 2,
belowBarData: BarAreaData(show: true, color: Colors.red[50]), // 填充色
),
// 舒张压配置...
],
)
性能优化技巧:
- 使用
memoize缓存计算结果 - 限制渲染数据点为最近30条
- 在列表滚动时暂停图表动画
3.2 健康状态评估算法
根据中国高血压防治指南标准实现:
dart复制BloodPressureStatus evaluateStatus(int systolic, int diastolic) {
if (systolic >= 140 || diastolic >= 90) {
return BloodPressureStatus.high;
} else if (systolic < 90 || diastolic < 60) {
return BloodPressureStatus.low;
} else if (systolic >= 120 || diastolic >= 80) {
return BloodPressureStatus.normalHigh;
}
return BloodPressureStatus.normal;
}
状态显示采用颜色编码:
- 红色:高血压(≥140/90)
- 橙色:正常高值(120-139/80-89)
- 绿色:理想血压
- 蓝色:低血压
4. 用户体验优化实践
4.1 录入表单的防错设计
- 输入限制:
dart复制TextField(
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(3),
],
validator: (value) {
if (value == null || int.parse(value) > 250) {
return '请输入有效血压值';
}
return null;
}
)
- 智能默认值:
- 自动填充上次记录值
- 根据当前时间提示晨峰/夜间血压
4.2 响应式布局适配
使用flutter_screenutil实现多设备适配:
dart复制// 初始化
ScreenUtil.init(
context,
designSize: const Size(375, 812),
);
// 使用示例
Container(
width: 100.w, // 等同于设计稿100px
height: 50.h,
margin: EdgeInsets.all(10.r), // 响应式圆角
)
5. 性能优化与调试
5.1 内存管理要点
- Provider使用规范:
dart复制// 错误用法会导致内存泄漏
Consumer<HealthProvider>(
builder: (context, provider, _) {
return Text('${provider.records.length}');
}
)
// 正确用法应提取子组件
const RecordCountText();
class RecordCountText extends StatelessWidget {
const RecordCountText({super.key});
@override
Widget build(BuildContext context) {
return Consumer<HealthProvider>(
builder: (context, provider, _) {
return Text('${provider.records.length}');
}
);
}
}
5.2 常见问题排查
- 图表不更新问题:
- 检查数据源是否被正确监听
- 确认LineChartData是否创建新实例
- 验证spots数据是否按预期排序
- 性能问题检查清单:
- [ ] 是否在build方法中执行耗时操作
- [ ] 是否过度使用setState
- [ ] 是否存在不必要的全局重建
6. 扩展功能设计
6.1 数据导出方案
支持生成PDF报告的核心代码:
dart复制final pdf = pw.Document();
pdf.addPage(
pw.Page(
build: (context) {
return pw.Column(
children: [
pw.Text('血压记录报告', style: pw.TextStyle(fontSize: 20)),
pw.Divider(),
pw.ListView.builder(
itemBuilder: (_, index) {
final record = records[index];
return pw.Row(
children: [
pw.Text(DateFormat('yyyy-MM-dd').format(record.recordDate)),
pw.Text('${record.data['systolic']}/${record.data['diastolic']}'),
],
);
},
itemCount: records.length,
),
],
);
},
),
);
final file = File('${dir.path}/report.pdf');
await file.writeAsBytes(await pdf.save());
6.2 健康提醒功能
基于WorkManager实现定时提醒:
dart复制void scheduleReminder() {
Workmanager().registerPeriodicTask(
'bloodPressureReminder',
'showReminder',
frequency: const Duration(hours: 12),
constraints: Constraints(
networkType: NetworkType.not_required,
requiresBatteryNotLow: true,
),
);
}
7. 项目实践心得
在开发血压模块过程中,有几个关键经验值得分享:
-
医学准确性优先:血压等级划分必须严格遵循最新医学指南,我们邀请了心内科医生参与评审
-
极端值处理:遇到收缩压>200mmHg的数据时,系统会弹出警示对话框建议立即就医
-
数据同步策略:采用增量同步机制,每次只同步最近24小时的记录,减少流量消耗
-
无障碍设计:为视力不佳的用户添加了语音播报功能,可通过双击卡片触发
一个特别容易忽视的细节是时间戳处理。最初我们直接使用设备本地时间,导致多设备间数据不同步。后来改为统一使用服务器时间,并在录入时提示时区信息:
dart复制recordDate: DateTime.now().toUtc() // 统一使用UTC时间
图表渲染性能方面,经过测试发现:
- 100个数据点:60fps流畅
- 500个数据点:约45fps
- 1000个数据点:明显卡顿
因此最终方案是:
- 趋势图默认显示最近7天数据
- 详情页可选查看30天数据(启用降采样)