Flutter 框架跨平台鸿蒙开发 - 家庭健康档案管理系统开发教程
本教程详细介绍了Flutter家庭健康档案管理系统的完整开发过程。
Flutter家庭健康档案管理系统开发教程
项目概述
随着人们对健康管理意识的不断提高,家庭健康档案管理变得越来越重要。本教程将带你开发一个功能完整的家庭健康档案管理系统,帮助家庭成员更好地管理和跟踪健康信息。
运行效果图





应用特色
- 完整的家庭成员管理:支持添加和管理多个家庭成员的基本信息
- 详细的健康记录:记录体检、就诊、住院、疫苗、手术等各类健康记录
- 生命体征监测:跟踪血压、心率、体温、血糖等重要生命体征
- 智能健康分析:提供BMI计算、血压状态评估等健康指标分析
- 直观的数据展示:采用卡片式设计,信息展示清晰直观
- 统计分析功能:提供家庭健康数据的统计和分析
- 响应式设计:适配不同屏幕尺寸,提供良好的用户体验
技术栈
- 框架:Flutter 3.x
- 开发语言:Dart
- UI设计:Material Design 3
- 状态管理:StatefulWidget
- 数据存储:内存存储(可扩展为本地数据库)
核心功能模块
1. 家庭成员管理
- 成员基本信息录入和展示
- 关系标识和头像管理
- BMI计算和健康状态评估
- 联系方式和紧急联系人管理
2. 健康记录管理
- 多类型健康记录支持(体检、就诊、住院等)
- 详细的医疗信息记录
- 症状和用药记录
- 医院和医生信息管理
3. 生命体征监测
- 血压、心率、体温等关键指标记录
- 血糖和体重变化跟踪
- 健康状态自动评估
- 历史数据趋势分析
4. 统计分析功能
- 家庭健康数据统计
- 最近记录快速查看
- 健康趋势分析
- 数据可视化展示
数据模型设计
FamilyMember(家庭成员)模型
class FamilyMember {
final String id; // 成员唯一标识
final String name; // 姓名
final String relationship; // 家庭关系
final DateTime birthDate; // 出生日期
final String gender; // 性别
final String bloodType; // 血型
final double height; // 身高
final double weight; // 体重
final String phone; // 电话号码
final String emergencyContact; // 紧急联系人
final List<String> allergies; // 过敏史
final List<String> chronicDiseases; // 慢性疾病
final List<HealthRecord> healthRecords; // 健康记录
final String avatar; // 头像
}
FamilyMember模型包含了家庭成员的完整信息,通过计算属性提供年龄、BMI等衍生数据。relationshipColor和relationshipIcon属性根据家庭关系返回对应的颜色和图标,提升界面的视觉识别度。
HealthRecord(健康记录)模型
class HealthRecord {
final String id; // 记录唯一标识
final String memberId; // 关联成员ID
final DateTime date; // 记录日期
final String type; // 记录类型
final String title; // 记录标题
final String description; // 详细描述
final String hospital; // 医院名称
final String doctor; // 医生姓名
final List<String> symptoms; // 症状列表
final String diagnosis; // 诊断结果
final List<String> medications; // 用药记录
final String notes; // 备注信息
final List<String> attachments; // 附件列表
}
健康记录模型支持多种类型的医疗记录,包括体检、就诊、住院、疫苗接种和手术等。通过typeColor和typeIcon属性为不同类型的记录提供视觉区分。
VitalSigns(生命体征)模型
class VitalSigns {
final String id; // 记录唯一标识
final String memberId; // 关联成员ID
final DateTime date; // 测量日期
final double? bloodPressureSystolic; // 收缩压
final double? bloodPressureDiastolic; // 舒张压
final double? heartRate; // 心率
final double? temperature; // 体温
final double? bloodSugar; // 血糖
final double? weight; // 体重
final String notes; // 备注
}
生命体征模型记录各项重要的健康指标,支持血压状态自动评估,通过bloodPressureStatus和bloodPressureColor属性提供健康状态的直观显示。
项目结构设计
lib/
├── main.dart # 应用入口文件
├── models/ # 数据模型
│ ├── family_member.dart # 家庭成员模型
│ ├── health_record.dart # 健康记录模型
│ └── vital_signs.dart # 生命体征模型
├── screens/ # 页面文件
│ ├── home_screen.dart # 主页面
│ ├── members_screen.dart # 成员页面
│ ├── records_screen.dart # 记录页面
│ ├── vitals_screen.dart # 体征页面
│ └── stats_screen.dart # 统计页面
├── widgets/ # 自定义组件
│ ├── member_card.dart # 成员卡片
│ ├── record_card.dart # 记录卡片
│ ├── vital_card.dart # 体征卡片
│ └── stat_card.dart # 统计卡片
└── services/ # 业务逻辑
└── health_service.dart # 健康数据服务
主界面设计与实现
底部导航栏设计
应用采用四个主要功能模块的底部导航设计:
- 成员:显示所有家庭成员信息
- 健康记录:展示选定成员的健康记录
- 生命体征:显示生命体征监测数据
- 统计:提供健康数据统计分析
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() => _selectedIndex = index);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.family_restroom), label: '成员'),
NavigationDestination(icon: Icon(Icons.medical_information), label: '健康记录'),
NavigationDestination(icon: Icon(Icons.monitor_heart), label: '生命体征'),
NavigationDestination(icon: Icon(Icons.analytics), label: '统计'),
],
),
应用栏设计
appBar: AppBar(
title: const Text('家庭健康档案'),
backgroundColor: Colors.teal.withValues(alpha: 0.1),
actions: [
IconButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('搜索功能开发中...')),
);
},
icon: const Icon(Icons.search),
),
IconButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('设置功能开发中...')),
);
},
icon: const Icon(Icons.settings),
),
],
),
应用栏采用简洁设计,使用青色作为主题色,与健康医疗的主题相符。提供搜索和设置功能的入口,为后续功能扩展预留接口。
家庭成员管理页面
成员列表展示
家庭成员页面以列表形式展示所有成员信息:
Widget _buildMembersPage() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'家庭成员',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'共${_familyMembers.length}位成员',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: _familyMembers.length,
itemBuilder: (context, index) {
return _buildMemberCard(_familyMembers[index]);
},
),
),
],
),
);
}
成员卡片设计
每个家庭成员以卡片形式展示关键信息:
Widget _buildMemberCard(FamilyMember member) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () => _showMemberDetail(member),
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
// 成员头像区域
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: member.relationshipColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: member.relationshipColor.withValues(alpha: 0.3)),
),
child: Icon(
member.relationshipIcon,
color: member.relationshipColor,
size: 30,
),
),
const SizedBox(width: 16),
// 成员基本信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
member.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'${member.relationship} · ${member.age}岁 · ${member.gender}',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 4),
Text(
'血型:${member.bloodType} · BMI:${member.bmi.toStringAsFixed(1)}',
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
],
),
),
// 健康状态和记录数量
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: member.relationshipColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
member.bmiCategory,
style: TextStyle(
fontSize: 12,
color: member.relationshipColor,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 4),
Text(
'${member.healthRecords.length}条记录',
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
],
),
],
),
),
),
);
}
成员卡片设计特点:
- 视觉识别:根据家庭关系使用不同颜色和图标
- 关键信息:显示姓名、关系、年龄、性别等基本信息
- 健康指标:展示血型、BMI等健康相关数据
- 记录统计:显示该成员的健康记录数量
- 交互设计:点击卡片可查看详细信息
成员详情对话框
点击成员卡片可以查看完整的成员档案信息:
void _showMemberDetail(FamilyMember member) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('${member.name} 档案'),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// 成员头像展示区域
Container(
width: double.infinity,
height: 120,
decoration: BoxDecoration(
color: member.relationshipColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: member.relationshipColor.withValues(alpha: 0.3)),
),
child: Icon(
member.relationshipIcon,
size: 60,
color: member.relationshipColor,
),
),
const SizedBox(height: 16),
// 详细信息展示
_buildDetailRow('姓名', member.name),
_buildDetailRow('关系', member.relationship),
_buildDetailRow('年龄', '${member.age}岁'),
_buildDetailRow('性别', member.gender),
_buildDetailRow('血型', member.bloodType),
_buildDetailRow('身高', '${member.height.toStringAsFixed(1)}cm'),
_buildDetailRow('体重', '${member.weight.toStringAsFixed(1)}kg'),
_buildDetailRow('BMI', '${member.bmi.toStringAsFixed(1)} (${member.bmiCategory})'),
_buildDetailRow('电话', member.phone),
_buildDetailRow('紧急联系人', member.emergencyContact),
// 过敏史和慢性疾病
if (member.allergies.isNotEmpty) ...[
const Text('过敏史:', style: TextStyle(fontWeight: FontWeight.bold)),
...member.allergies.map((allergy) => Text('• $allergy')),
],
if (member.chronicDiseases.isNotEmpty) ...[
const Text('慢性疾病:', style: TextStyle(fontWeight: FontWeight.bold)),
...member.chronicDiseases.map((disease) => Text('• $disease')),
],
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('关闭'),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
setState(() {
_selectedMemberId = member.id;
_selectedIndex = 1;
});
},
child: const Text('查看记录'),
),
],
),
);
}
健康记录管理
成员选择器
健康记录页面顶部提供成员选择功能:
Widget _buildMemberSelector() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.teal.withValues(alpha: 0.05),
border: Border(
bottom: BorderSide(color: Colors.grey.withValues(alpha: 0.3))),
),
child: Row(
children: [
const Text('选择成员:', style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(width: 12),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _familyMembers.map((member) {
final isSelected = _selectedMemberId == member.id;
return Padding(
padding: const EdgeInsets.only(right: 8),
child: FilterChip(
label: Text(member.name),
selected: isSelected,
onSelected: (selected) {
setState(() {
_selectedMemberId = member.id;
});
},
),
);
}).toList(),
),
),
),
],
),
);
}
健康记录卡片
每条健康记录以卡片形式展示:
Widget _buildHealthRecordCard(HealthRecord record) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: InkWell(
onTap: () => _showHealthRecordDetail(record),
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 记录类型和日期
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: record.typeColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: record.typeColor.withValues(alpha: 0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(record.typeIcon, size: 16, color: record.typeColor),
const SizedBox(width: 4),
Text(
record.type,
style: TextStyle(
fontSize: 12,
color: record.typeColor,
fontWeight: FontWeight.w500,
),
),
],
),
),
const Spacer(),
Text(
'${record.date.year}/${record.date.month}/${record.date.day}',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
const SizedBox(height: 12),
// 记录标题和描述
Text(
record.title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
record.description,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
// 医院和医生信息
Row(
children: [
Icon(Icons.local_hospital, size: 16, color: Colors.grey[600]),
const SizedBox(width: 4),
Text(record.hospital, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
const SizedBox(width: 16),
Icon(Icons.person, size: 16, color: Colors.grey[600]),
const SizedBox(width: 4),
Text(record.doctor, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
],
),
// 症状标签
if (record.symptoms.isNotEmpty) ...[
const SizedBox(height: 8),
Wrap(
spacing: 4,
children: record.symptoms.map((symptom) {
return Chip(
label: Text(symptom, style: const TextStyle(fontSize: 10)),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
}).toList(),
),
],
],
),
),
),
);
}
健康记录卡片特点:
- 类型标识:不同类型的记录使用不同颜色和图标
- 关键信息:显示标题、描述、日期等核心信息
- 医疗信息:展示医院、医生等相关信息
- 症状标签:以标签形式展示症状信息
- 交互设计:点击可查看详细信息
生命体征监测
生命体征卡片设计
生命体征页面展示各项健康指标:
Widget _buildVitalSignsCard(VitalSigns vital) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 日期和血压状态
Row(
children: [
Icon(Icons.monitor_heart, color: Colors.teal, size: 20),
const SizedBox(width: 8),
Text(
'${vital.date.year}/${vital.date.month}/${vital.date.day}',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: vital.bloodPressureColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: vital.bloodPressureColor.withValues(alpha: 0.3)),
),
child: Text(
vital.bloodPressureStatus,
style: TextStyle(
fontSize: 12,
color: vital.bloodPressureColor,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 16),
// 生命体征指标网格
GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 2.5,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: [
_buildVitalItem('血压', '${vital.bloodPressureSystolic?.toStringAsFixed(0) ?? '--'}/${vital.bloodPressureDiastolic?.toStringAsFixed(0) ?? '--'}', 'mmHg', Icons.favorite, Colors.red),
_buildVitalItem('心率', vital.heartRate?.toStringAsFixed(0) ?? '--', 'bpm', Icons.monitor_heart, Colors.pink),
_buildVitalItem('体温', vital.temperature?.toStringAsFixed(1) ?? '--', '°C', Icons.thermostat, Colors.orange),
_buildVitalItem('血糖', vital.bloodSugar?.toStringAsFixed(1) ?? '--', 'mmol/L', Icons.water_drop, Colors.blue),
],
),
],
),
),
);
}
生命体征指标项
每个生命体征指标以独立的卡片形式展示:
Widget _buildVitalItem(String label, String value, String unit, IconData icon, Color color) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: color.withValues(alpha: 0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: color, size: 16),
const SizedBox(width: 4),
Text(
label,
style: TextStyle(
fontSize: 12,
color: color,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
value,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(width: 2),
Text(
unit,
style: TextStyle(
fontSize: 10,
color: color.withValues(alpha: 0.7),
),
),
],
),
],
),
);
}
生命体征监测功能特点:
- 多指标支持:血压、心率、体温、血糖等关键指标
- 状态评估:自动评估血压状态(正常、偏高、高血压)
- 视觉区分:不同指标使用不同颜色标识
- 数据完整性:支持部分指标缺失的情况
- 历史记录:按时间顺序展示历史测量数据
统计分析功能
统计概览页面
统计页面提供家庭健康数据的整体概览:
Widget _buildStatisticsPage() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'健康统计',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
_buildStatisticsGrid(),
const SizedBox(height: 24),
const Text(
'最近记录',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Expanded(
child: _buildRecentRecords(),
),
],
),
);
}
统计数据网格
统计数据以网格形式展示关键指标:
Widget _buildStatisticsGrid() {
final totalMembers = _familyMembers.length;
final totalRecords = _familyMembers
.fold(0, (sum, member) => sum + member.healthRecords.length);
final totalVitalSigns = _vitalSigns.length;
final averageAge = _familyMembers.isEmpty
? 0
: _familyMembers.fold(0, (sum, member) => sum + member.age) /
_familyMembers.length;
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 1.5,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: [
_buildStatCard('家庭成员', totalMembers.toString(), Icons.family_restroom, Colors.blue),
_buildStatCard('健康记录', totalRecords.toString(), Icons.medical_information, Colors.green),
_buildStatCard('体征记录', totalVitalSigns.toString(), Icons.monitor_heart, Colors.orange),
_buildStatCard('平均年龄', averageAge.toStringAsFixed(0), Icons.cake, Colors.purple),
],
);
}
Widget _buildStatCard(String title, String value, IconData icon, Color color) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color, size: 32),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: color,
),
),
const SizedBox(height: 4),
Text(
title,
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
),
);
}
统计功能提供以下关键指标:
- 家庭成员:家庭成员总数统计
- 健康记录:所有成员健康记录总数
- 体征记录:生命体征测量记录总数
- 平均年龄:家庭成员平均年龄计算
数据生成与管理
家庭成员数据生成
系统自动生成测试数据,包含6位不同关系的家庭成员:
void _generateFamilyMembers() {
final relationships = ['父亲', '母亲', '儿子', '女儿', '爷爷', '奶奶'];
final names = ['张明', '李华', '张小明', '张小丽', '张爷爷', '李奶奶'];
final bloodTypes = ['A', 'B', 'AB', 'O'];
final allergiesList = ['花粉过敏', '海鲜过敏', '药物过敏', '尘螨过敏'];
final chronicDiseasesList = ['高血压', '糖尿病', '心脏病', '哮喘'];
final random = Random();
for (int i = 0; i < 6; i++) {
final birthYear = 1950 + random.nextInt(50);
final birthMonth = 1 + random.nextInt(12);
final birthDay = 1 + random.nextInt(28);
// 随机生成过敏史
final allergies = <String>[];
if (random.nextBool()) {
allergies.add(allergiesList[random.nextInt(allergiesList.length)]);
}
// 随机生成慢性疾病
final chronicDiseases = <String>[];
if (random.nextBool()) {
chronicDiseases.add(chronicDiseasesList[random.nextInt(chronicDiseasesList.length)]);
}
_familyMembers.add(FamilyMember(
id: 'member_$i',
name: names[i],
relationship: relationships[i],
birthDate: DateTime(birthYear, birthMonth, birthDay),
gender: i % 2 == 0 ? '男' : '女',
bloodType: '${bloodTypes[random.nextInt(bloodTypes.length)]}型',
height: 150 + random.nextDouble() * 30,
weight: 50 + random.nextDouble() * 30,
phone: '138****${1000 + random.nextInt(9000)}',
emergencyContact: '139****${1000 + random.nextInt(9000)}',
allergies: allergies,
chronicDiseases: chronicDiseases,
healthRecords: _generateHealthRecords('member_$i'),
avatar: 'avatar_$i.jpg',
));
}
}
健康记录数据生成
为每个家庭成员生成多条健康记录:
List<HealthRecord> _generateHealthRecords(String memberId) {
final types = ['体检', '就诊', '住院', '疫苗', '手术'];
final hospitals = ['市人民医院', '中医院', '妇幼保健院', '社区卫生服务中心'];
final doctors = ['张医生', '李医生', '王医生', '刘医生'];
final symptoms = ['发热', '咳嗽', '头痛', '腹痛', '乏力'];
final medications = ['阿莫西林', '布洛芬', '维生素C', '感冒灵'];
final random = Random();
final records = <HealthRecord>[];
for (int i = 0; i < 3 + random.nextInt(5); i++) {
final recordType = types[random.nextInt(types.length)];
// 随机选择症状
final selectedSymptoms = <String>[];
for (int j = 0; j < 1 + random.nextInt(3); j++) {
final symptom = symptoms[random.nextInt(symptoms.length)];
if (!selectedSymptoms.contains(symptom)) {
selectedSymptoms.add(symptom);
}
}
// 随机选择药物
final selectedMedications = <String>[];
for (int j = 0; j < 1 + random.nextInt(3); j++) {
final medication = medications[random.nextInt(medications.length)];
if (!selectedMedications.contains(medication)) {
selectedMedications.add(medication);
}
}
records.add(HealthRecord(
id: 'record_${memberId}_$i',
memberId: memberId,
date: DateTime.now().subtract(Duration(days: random.nextInt(365))),
type: recordType,
title: '$recordType记录',
description: '$recordType相关的详细描述',
hospital: hospitals[random.nextInt(hospitals.length)],
doctor: doctors[random.nextInt(doctors.length)],
symptoms: selectedSymptoms,
diagnosis: '根据症状诊断结果',
medications: selectedMedications,
notes: '医生建议和注意事项',
attachments: ['report_$i.pdf', 'image_$i.jpg'],
));
}
return records;
}
生命体征数据生成
为每个成员生成历史生命体征数据:
void _generateVitalSigns() {
final random = Random();
for (final member in _familyMembers) {
for (int i = 0; i < 10; i++) {
_vitalSigns.add(VitalSigns(
id: 'vital_${member.id}_$i',
memberId: member.id,
date: DateTime.now().subtract(Duration(days: i * 7)),
bloodPressureSystolic: 110 + random.nextDouble() * 40,
bloodPressureDiastolic: 70 + random.nextDouble() * 20,
heartRate: 60 + random.nextDouble() * 40,
temperature: 36.0 + random.nextDouble() * 2,
bloodSugar: 4.0 + random.nextDouble() * 4,
weight: member.weight + (random.nextDouble() - 0.5) * 5,
notes: '体征正常,无异常情况',
));
}
}
}
用户界面设计原则
Material Design 3 应用
应用全面采用Material Design 3设计规范:
- 颜色系统:使用青色(teal)作为主题色,符合医疗健康应用的视觉特征
- 组件设计:使用最新的Material 3组件,如NavigationBar、FilterChip等
- 视觉层次:通过不同的字体大小、颜色深浅建立清晰的信息层次
- 交互反馈:所有可点击元素都提供适当的视觉反馈和状态变化
健康主题设计
针对健康管理应用的特殊需求:
-
颜色语义化:
- 绿色:正常、健康状态
- 橙色:警告、需要注意
- 红色:异常、需要关注
- 蓝色:信息、数据展示
-
图标系统:
- 使用医疗相关的图标(心率、体温计、医院等)
- 家庭关系图标区分不同成员
- 健康记录类型图标标识
-
数据可视化:
- 生命体征数据网格化展示
- 健康状态标签化显示
- 统计数据卡片化呈现
响应式布局设计
// 网格布局自适应
GridView.count(
crossAxisCount: 2,
childAspectRatio: 2.5,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: [...],
)
// 弹性布局
Row(
children: [
Expanded(child: widget1),
const SizedBox(width: 16),
Expanded(child: widget2),
],
)
健康数据分析功能
BMI计算与评估
系统自动计算每个成员的BMI并提供健康评估:
double get bmi {
if (height <= 0) return 0;
return weight / ((height / 100) * (height / 100));
}
String get bmiCategory {
final bmiValue = bmi;
if (bmiValue < 18.5) return '偏瘦';
if (bmiValue < 24) return '正常';
if (bmiValue < 28) return '超重';
return '肥胖';
}
BMI评估标准:
- 偏瘦:BMI < 18.5
- 正常:18.5 ≤ BMI < 24
- 超重:24 ≤ BMI < 28
- 肥胖:BMI ≥ 28
血压状态评估
系统根据血压值自动评估健康状态:
String get bloodPressureStatus {
if (bloodPressureSystolic == null || bloodPressureDiastolic == null) {
return '未测量';
}
final systolic = bloodPressureSystolic!;
final diastolic = bloodPressureDiastolic!;
if (systolic < 120 && diastolic < 80) return '正常';
if (systolic < 140 && diastolic < 90) return '偏高';
return '高血压';
}
Color get bloodPressureColor {
switch (bloodPressureStatus) {
case '正常':
return Colors.green;
case '偏高':
return Colors.orange;
case '高血压':
return Colors.red;
default:
return Colors.grey;
}
}
血压评估标准:
- 正常:收缩压 < 120 且 舒张压 < 80
- 偏高:收缩压 < 140 且 舒张压 < 90
- 高血压:收缩压 ≥ 140 或 舒张压 ≥ 90
年龄计算
系统根据出生日期自动计算准确年龄:
int get age {
final now = DateTime.now();
int age = now.year - birthDate.year;
if (now.month < birthDate.month ||
(now.month == birthDate.month && now.day < birthDate.day)) {
age--;
}
return age;
}
状态管理与数据流
StatefulWidget状态管理
应用使用StatefulWidget进行状态管理:
class _FamilyHealthHomePageState extends State<FamilyHealthHomePage> {
int _selectedIndex = 0;
final List<FamilyMember> _familyMembers = [];
final List<VitalSigns> _vitalSigns = [];
String _selectedMemberId = '';
void initState() {
super.initState();
_generateFamilyMembers();
_generateVitalSigns();
}
// 状态更新方法
void _updateSelectedIndex(int index) {
setState(() {
_selectedIndex = index;
});
}
void _updateSelectedMember(String memberId) {
setState(() {
_selectedMemberId = memberId;
});
}
}
数据关联管理
通过ID关联不同数据实体:
// 获取指定成员的健康记录
final selectedMember = _familyMembers
.firstWhere((member) => member.id == _selectedMemberId);
final records = selectedMember.healthRecords;
// 获取指定成员的生命体征
final vitalSigns = _vitalSigns
.where((vital) => vital.memberId == _selectedMemberId)
.toList();
// 获取最近的健康记录
final allRecords = <HealthRecord>[];
for (final member in _familyMembers) {
allRecords.addAll(member.healthRecords);
}
allRecords.sort((a, b) => b.date.compareTo(a.date));
性能优化策略
列表渲染优化
使用ListView.builder进行高效列表渲染:
ListView.builder(
itemCount: records.length,
itemBuilder: (context, index) {
return _buildHealthRecordCard(records[index]);
},
)
条件渲染优化
避免不必要的组件渲染:
if (records.isEmpty) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.medical_information, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无健康记录', style: TextStyle(color: Colors.grey)),
],
),
);
}
状态更新优化
精确控制状态更新范围:
setState(() {
_selectedMemberId = member.id; // 只更新必要的状态
});
扩展功能建议
数据持久化
可以集成以下数据存储方案:
-
SQLite数据库:
dependencies: sqflite: ^2.3.0 path: ^1.8.3 // 创建数据库表 CREATE TABLE family_members ( id TEXT PRIMARY KEY, name TEXT NOT NULL, relationship TEXT, birth_date TEXT, gender TEXT, blood_type TEXT, height REAL, weight REAL, phone TEXT, emergency_contact TEXT ); -
SharedPreferences:
dependencies: shared_preferences: ^2.2.2 // 存储用户偏好 final prefs = await SharedPreferences.getInstance(); await prefs.setString('selected_member_id', memberId);
图表数据可视化
dependencies:
fl_chart: ^0.65.0
// 血压趋势图
Widget _buildBloodPressureChart() {
return LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(
spots: vitalSigns.map((vital) => FlSpot(
vital.date.millisecondsSinceEpoch.toDouble(),
vital.bloodPressureSystolic ?? 0,
)).toList(),
isCurved: true,
color: Colors.red,
),
],
),
);
}
健康提醒功能
dependencies:
flutter_local_notifications: ^16.3.0
// 设置体检提醒
void _scheduleHealthCheckReminder() {
final notification = FlutterLocalNotificationsPlugin();
notification.schedule(
0,
'健康提醒',
'该进行年度体检了',
DateTime.now().add(const Duration(days: 365)),
const NotificationDetails(
android: AndroidNotificationDetails(
'health_channel',
'健康提醒',
channelDescription: '健康相关提醒通知',
),
),
);
}
数据导出功能
dependencies:
pdf: ^3.10.7
path_provider: ^2.1.2
// 导出健康报告
Future<void> _exportHealthReport(FamilyMember member) async {
final pdf = pw.Document();
pdf.addPage(
pw.Page(
build: (pw.Context context) {
return pw.Column(
children: [
pw.Text('${member.name}健康档案',
style: pw.TextStyle(fontSize: 24)),
pw.SizedBox(height: 20),
// 添加健康记录内容
],
);
},
),
);
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/${member.name}_health_report.pdf');
await file.writeAsBytes(await pdf.save());
}
测试策略
单元测试
// test/models/family_member_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:your_app/models/family_member.dart';
void main() {
group('FamilyMember Tests', () {
test('Age calculation test', () {
final member = FamilyMember(
id: 'test_1',
name: '测试用户',
relationship: '父亲',
birthDate: DateTime(1980, 5, 15),
gender: '男',
bloodType: 'A型',
height: 175.0,
weight: 70.0,
phone: '13800000000',
emergencyContact: '13900000000',
allergies: [],
chronicDiseases: [],
healthRecords: [],
avatar: 'avatar.jpg',
);
expect(member.age, greaterThan(40));
});
test('BMI calculation test', () {
final member = FamilyMember(
// ... 其他属性
height: 175.0,
weight: 70.0,
// ... 其他属性
);
expect(member.bmi, closeTo(22.86, 0.01));
expect(member.bmiCategory, '正常');
});
});
}
集成测试
// integration_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:your_app/main.dart' as app;
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Family Health App Integration Tests', () {
testWidgets('Navigation between tabs test', (tester) async {
app.main();
await tester.pumpAndSettle();
// 测试底部导航
await tester.tap(find.text('健康记录'));
await tester.pumpAndSettle();
expect(find.text('选择成员:'), findsOneWidget);
await tester.tap(find.text('生命体征'));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.monitor_heart), findsWidgets);
await tester.tap(find.text('统计'));
await tester.pumpAndSettle();
expect(find.text('健康统计'), findsOneWidget);
});
testWidgets('Member detail dialog test', (tester) async {
app.main();
await tester.pumpAndSettle();
// 点击第一个成员卡片
await tester.tap(find.byType(Card).first);
await tester.pumpAndSettle();
// 验证详情对话框显示
expect(find.text('档案'), findsOneWidget);
expect(find.text('关闭'), findsOneWidget);
expect(find.text('查看记录'), findsOneWidget);
});
});
}
部署与发布
鸿蒙系统部署
-
环境配置:
# 确保Flutter支持鸿蒙 flutter doctor flutter create --platforms ohos . -
构建配置:
// ohos/entry/src/main/module.json5 { "module": { "name": "entry", "type": "entry", "description": "家庭健康档案管理系统", "mainElement": "EntryAbility", "deviceTypes": ["phone", "tablet"], "abilities": [ { "name": "EntryAbility", "label": "家庭健康档案", "icon": "$media:icon" } ] } } -
权限配置:
{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET", "reason": "网络访问权限" }, { "name": "ohos.permission.WRITE_MEDIA", "reason": "存储权限" } ] } }
Android/iOS部署
-
Android配置:
# android/app/build.gradle android { compileSdkVersion 33 defaultConfig { applicationId "com.example.family_health" minSdkVersion 21 targetSdkVersion 33 versionCode 1 versionName "1.0.0" } } -
iOS配置:
<!-- ios/Runner/Info.plist --> <key>CFBundleDisplayName</key> <string>家庭健康档案</string> <key>NSHealthShareUsageDescription</key> <string>需要访问健康数据来记录家庭成员健康信息</string>
总结
本教程详细介绍了Flutter家庭健康档案管理系统的完整开发过程。该应用具有以下特点:
技术特色
- 跨平台支持:支持鸿蒙、Android、iOS三大平台
- 现代化UI:采用Material Design 3设计规范和健康主题
- 完整功能:涵盖成员管理、健康记录、生命体征监测、统计分析等核心功能
- 智能分析:提供BMI计算、血压评估等健康指标分析
- 良好架构:清晰的数据模型和组件设计
应用价值
- 家庭健康管理:帮助家庭更好地管理和跟踪健康信息
- 数据集中化:将分散的健康信息集中管理
- 趋势分析:通过历史数据分析健康趋势
- 便民服务:提供便捷的健康档案查询和管理
扩展潜力
- 数据同步:可以集成云存储实现多设备同步
- 智能提醒:可以添加健康提醒和预警功能
- 数据可视化:可以集成图表库实现数据可视化
- 医疗集成:可以与医疗机构系统对接
通过本教程的学习,开发者可以掌握Flutter在健康管理领域的应用开发技能,并能够开发出功能完整、用户体验良好的健康管理应用。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)