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   # 健康数据服务

主界面设计与实现

底部导航栏设计

应用采用四个主要功能模块的底部导航设计:

  1. 成员:显示所有家庭成员信息
  2. 健康记录:展示选定成员的健康记录
  3. 生命体征:显示生命体征监测数据
  4. 统计:提供健康数据统计分析
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设计规范:

  1. 颜色系统:使用青色(teal)作为主题色,符合医疗健康应用的视觉特征
  2. 组件设计:使用最新的Material 3组件,如NavigationBar、FilterChip等
  3. 视觉层次:通过不同的字体大小、颜色深浅建立清晰的信息层次
  4. 交互反馈:所有可点击元素都提供适当的视觉反馈和状态变化

健康主题设计

针对健康管理应用的特殊需求:

  1. 颜色语义化

    • 绿色:正常、健康状态
    • 橙色:警告、需要注意
    • 红色:异常、需要关注
    • 蓝色:信息、数据展示
  2. 图标系统

    • 使用医疗相关的图标(心率、体温计、医院等)
    • 家庭关系图标区分不同成员
    • 健康记录类型图标标识
  3. 数据可视化

    • 生命体征数据网格化展示
    • 健康状态标签化显示
    • 统计数据卡片化呈现

响应式布局设计

// 网格布局自适应
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; // 只更新必要的状态
});

扩展功能建议

数据持久化

可以集成以下数据存储方案:

  1. 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
    );
    
  2. 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);
    });
  });
}

部署与发布

鸿蒙系统部署

  1. 环境配置

    # 确保Flutter支持鸿蒙
    flutter doctor
    flutter create --platforms ohos .
    
  2. 构建配置

    // ohos/entry/src/main/module.json5
    {
      "module": {
        "name": "entry",
        "type": "entry",
        "description": "家庭健康档案管理系统",
        "mainElement": "EntryAbility",
        "deviceTypes": ["phone", "tablet"],
        "abilities": [
          {
            "name": "EntryAbility",
            "label": "家庭健康档案",
            "icon": "$media:icon"
          }
        ]
      }
    }
    
  3. 权限配置

    {
      "module": {
        "requestPermissions": [
          {
            "name": "ohos.permission.INTERNET",
            "reason": "网络访问权限"
          },
          {
            "name": "ohos.permission.WRITE_MEDIA",
            "reason": "存储权限"
          }
        ]
      }
    }
    

Android/iOS部署

  1. Android配置

    # android/app/build.gradle
    android {
        compileSdkVersion 33
        defaultConfig {
            applicationId "com.example.family_health"
            minSdkVersion 21
            targetSdkVersion 33
            versionCode 1
            versionName "1.0.0"
        }
    }
    
  2. 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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐