在这里插入图片描述

去年冬天,我陪着外婆去医院复查。医生开了三种药,叮嘱要按时服用。回到家后,外婆拿出一个小本子,认真地记录着每种药的名字和服用时间。她说:"年纪大了,记性不好,不写下来就忘了。"看着她颤抖的手写字,我突然意识到,我们需要为老年人做一个真正好用的用药管理工具。

在实际开发过程中,我们发现老年用户的需求很简单:记得住、看得清、用得会。基于这个原则,我们设计了一套简洁实用的用药管理系统。

药物数据模型设计

首先定义药物的基本信息结构:

class Medication {
  final String id;
  final String name;
  final String dosage;
  final List<String> times;
  final int remainingDays;
  
  Medication({
    required this.id,
    required this.name,
    required this.dosage,
    required this.times,
    required this.remainingDays,
  });
}

这个数据模型包含了老年用户最关心的信息。name 是药物名称,我们允许用户用自己习惯的叫法,比如"降压药"而不是复杂的医学名称。dosage 记录剂量信息,times 数组存储每天的服药时间点,remainingDays 帮助用户了解药物还能吃多久。

接下来实现数据序列化方法:

Map<String, dynamic> toJson() {
  return {
    'id': id,
    'name': name,
    'dosage': dosage,
    'times': times,
    'remainingDays': remainingDays,
  };
}

💾 数据持久化的重要性:序列化方法让药物信息能够保存到本地存储中。对于老年用户来说,最怕的就是辛苦输入的信息突然消失,所以我们必须确保数据的可靠保存。

控制器业务逻辑实现

创建用药管理的核心控制器:

class MedicationController extends GetxController {
  final RxList<Medication> medications = <Medication>[].obs;
  final RxBool isLoading = false.obs;
  
  
  void onInit() {
    super.onInit();
    loadMedications();
  }
}

⚡ 响应式编程的优势:使用GetX的响应式变量,当药物列表发生变化时,界面会自动更新。这种声明式的编程方式让代码更简洁,也避免了手动刷新界面的繁琐操作。

实现药物数据加载功能:

Future<void> loadMedications() async {
  try {
    isLoading.value = true;
    
    // 模拟从本地存储加载数据
    await Future.delayed(Duration(milliseconds: 500));
    
    medications.value = [
      Medication(
        id: '001',
        name: '阿司匹林',
        dosage: '100mg,每次1片',
        times: ['08:00', '20:00'],
        remainingDays: 15,
      ),
    ];
  } finally {
    isLoading.value = false;
  }
}

这里我们提供了一个真实的药物示例。阿司匹林是老年人常用的心血管保护药物,每天两次服用是典型的用药方案。剩余15天给用户一个明确的购药时间提醒。

🎯 用户体验考虑:加载过程中显示loading状态,让用户知道系统正在工作。即使是很快的操作,适当的反馈也能提升用户的信任感。

添加新药物的方法:

void addMedication(Medication medication) {
  medications.insert(0, medication);
  Get.snackbar(
    '添加成功',
    '${medication.name} 已加入用药清单',
    backgroundColor: Colors.green.shade100,
    colorText: Colors.green.shade800,
  );
}

✅ 即时反馈设计:每次添加药物后立即显示成功提示,使用绿色配色传达积极信息。这种即时反馈让老年用户能够确认操作成功,减少重复操作的困扰。

实现服药记录功能:

void markAsTaken(String medicationId) {
  final now = DateTime.now();
  final timeString = '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}';
  
  // 保存服药记录
  _saveIntakeRecord(medicationId, now);
  
  Get.snackbar(
    '服药已记录',
    '服药时间:$timeString',
    icon: Icon(Icons.check_circle, color: Colors.green),
  );
}

这个功能记录用户的实际服药时间,为医生提供准确的用药依从性数据。时间格式化确保显示的时间清晰易读,图标提示增强了视觉反馈效果。

📊 数据价值体现:服药记录不仅帮助用户回顾用药情况,更重要的是为医生调整治疗方案提供依据。这种数据驱动的医疗服务正是智慧养老的核心价值。

用户界面设计

创建主界面结构:

class MedicationView extends GetView<MedicationController> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('用药管理', style: TextStyle(fontSize: 20.sp)),
        backgroundColor: Color(0xFF4CAF50),
      ),
      body: _buildBody(),
    );
  }
}

🎨 适老化设计原则:AppBar使用20sp的大字体,确保老年用户能够清晰阅读。绿色主题色彩象征健康和生命力,给用户带来积极的心理暗示。

实现药物列表展示:

Widget _buildBody() {
  return Obx(() {
    if (controller.isLoading.value) {
      return Center(child: CircularProgressIndicator());
    }
    
    return ListView.builder(
      padding: EdgeInsets.all(16.w),
      itemCount: controller.medications.length,
      itemBuilder: (context, index) {
        return _buildMedicationCard(controller.medications[index]);
      },
    );
  });
}

这里使用了响应式列表构建,当药物数据变化时自动重建界面。ListView.builder提供了良好的性能,即使药物列表很长也能流畅滚动。

🔄 性能优化策略:ListView.builder只渲染可见的列表项,这种懒加载机制确保了应用在处理大量药物数据时依然保持流畅。对于可能有多种慢性病药物的老年用户来说,这种优化尤为重要。

设计药物信息卡片:

Widget _buildMedicationCard(Medication medication) {
  return Card(
    margin: EdgeInsets.only(bottom: 12.h),
    child: Padding(
      padding: EdgeInsets.all(16.w),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildCardHeader(medication),
          SizedBox(height: 12.h),
          _buildMedicationDetails(medication),
        ],
      ),
    ),
  );
}

📋 信息层次设计:卡片布局将每个药物的信息清晰分组,避免信息混乱。适当的内边距和间距让界面看起来整洁舒适,符合老年用户的视觉习惯。

实现卡片头部信息:

Widget _buildCardHeader(Medication medication) {
  return Row(
    children: [
      Icon(Icons.medication, size: 24.sp, color: Colors.blue),
      SizedBox(width: 12.w),
      Expanded(
        child: Text(
          medication.name,
          style: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
        ),
      ),
      _buildRemainingDaysChip(medication.remainingDays),
    ],
  );
}

药物名称使用18sp的粗体,确保老年用户能够快速识别。药物图标提供视觉提示,剩余天数标签放在显眼位置,方便用户及时了解库存情况。

👁️ 视觉引导设计:通过图标、字体大小和颜色的层次搭配,引导用户的视线按照重要性顺序浏览信息。这种设计减少了认知负担,让老年用户能够快速获取关键信息。

添加服药时间显示:

Widget _buildTimeChips(List<String> times) {
  return Wrap(
    spacing: 8.w,
    children: times.map((time) => Chip(
      label: Text(time, style: TextStyle(fontSize: 14.sp)),
      backgroundColor: Colors.blue.shade50,
    )).toList(),
  );
}

⏰ 时间信息可视化:使用Chip组件展示服药时间,每个时间点都有独立的标签。这种设计让用户一眼就能看到一天需要服药的所有时间点,避免遗漏。

提醒功能实现

创建本地通知服务:

class NotificationService {
  static Future<void> scheduleMedicationReminder({
    required String medicationName,
    required String time,
  }) async {
    final parts = time.split(':');
    final hour = int.parse(parts[0]);
    final minute = int.parse(parts[1]);
    
    // 设置每日重复提醒
    await _scheduleNotification(
      title: '用药提醒',
      body: '该服用 $medicationName 了',
      hour: hour,
      minute: minute,
    );
  }
}

这个服务解析用户设置的服药时间,创建系统级的定时提醒。每日重复确保用户不会因为忘记而错过服药时间。

🔔 可靠提醒机制:使用系统级通知确保即使应用被关闭,提醒依然能够准时到达。这种可靠性对于需要严格按时服药的老年用户来说至关重要。

实现库存预警功能:

void checkLowStock() {
  for (var medication in medications) {
    if (medication.remainingDays <= 3) {
      Get.snackbar(
        '库存不足',
        '${medication.name} 仅剩 ${medication.remainingDays} 天',
        backgroundColor: Colors.orange.shade100,
        duration: Duration(seconds: 5),
      );
    }
  }
}

📦 主动库存管理:当药物剩余量少于3天时自动提醒用户购买。选择3天作为阈值是经过用户调研确定的,既给用户留出充足的购药时间,又不会过早打扰用户。

数据持久化实现

使用SharedPreferences保存用药数据:

class StorageService {
  static const String _medicationsKey = 'medications';
  
  static Future<void> saveMedications(List<Medication> medications) async {
    final prefs = await SharedPreferences.getInstance();
    final jsonList = medications.map((m) => m.toJson()).toList();
    await prefs.setString(_medicationsKey, json.encode(jsonList));
  }
  
  static Future<List<Medication>> loadMedications() async {
    final prefs = await SharedPreferences.getInstance();
    final jsonString = prefs.getString(_medicationsKey);
    if (jsonString == null) return [];
    
    final jsonList = json.decode(jsonString) as List;
    return jsonList.map((json) => Medication.fromJson(json)).toList();
  }
}

💾 本地数据安全:使用SharedPreferences确保用药数据即使在应用重启后也不会丢失。JSON序列化让复杂的数据结构能够可靠地存储和读取。

用户体验优化

实现下拉刷新功能:

Widget _buildRefreshableList() {
  return RefreshIndicator(
    onRefresh: controller.loadMedications,
    child: ListView.builder(
      itemCount: controller.medications.length,
      itemBuilder: (context, index) {
        return _buildMedicationCard(controller.medications[index]);
      },
    ),
  );
}

🔄 直观的交互设计:下拉刷新是用户熟悉的操作方式,让老年用户也能轻松掌握。这种标准化的交互模式降低了学习成本。

添加空状态处理:

Widget _buildEmptyState() {
  return Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(Icons.medication, size: 64.sp, color: Colors.grey[400]),
        SizedBox(height: 16.h),
        Text('还没有添加药物', style: TextStyle(fontSize: 16.sp)),
        SizedBox(height: 8.h),
        Text('点击右下角按钮添加', style: TextStyle(color: Colors.grey)),
      ],
    ),
  );
}

🎯 友好的引导设计:空状态页面不仅告诉用户当前没有数据,还提供了明确的操作指引。这种设计避免了用户面对空白页面时的困惑。

添加药物对话框

实现添加药物的交互界面:

void _showAddMedicationDialog() {
  final nameController = TextEditingController();
  final dosageController = TextEditingController();
  
  Get.dialog(
    AlertDialog(
      title: Text('添加药物', style: TextStyle(fontSize: 18.sp)),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextField(
            controller: nameController,
            decoration: InputDecoration(
              labelText: '药物名称',
              hintText: '如:阿司匹林',
            ),
          ),
          SizedBox(height: 16.h),
          TextField(
            controller: dosageController,
            decoration: InputDecoration(
              labelText: '用法用量',
              hintText: '如:每次1片',
            ),
          ),
        ],
      ),
      actions: [
        TextButton(
          onPressed: () => Get.back(),
          child: Text('取消'),
        ),
        ElevatedButton(
          onPressed: () => _saveMedication(nameController, dosageController),
          child: Text('保存'),
        ),
      ],
    ),
  );
}

这个对话框提供了简洁的药物信息输入界面。大字体标题清晰的输入提示让老年用户能够轻松理解每个字段的含义。

📝 简化输入流程:只要求用户输入最基本的信息,避免复杂的表单。提示文字使用常见的药物名称作为示例,降低用户的理解门槛。

实现药物保存逻辑:

void _saveMedication(TextEditingController nameController, TextEditingController dosageController) {
  if (nameController.text.isEmpty || dosageController.text.isEmpty) {
    Get.snackbar('提示', '请填写完整信息');
    return;
  }
  
  final medication = Medication(
    id: DateTime.now().millisecondsSinceEpoch.toString(),
    name: nameController.text,
    dosage: dosageController.text,
    times: ['08:00'], // 默认早上8点
    remainingDays: 30, // 默认30天
  );
  
  controller.addMedication(medication);
  Get.back();
}

🛡️ 数据验证保护:在保存前检查必填字段,防止空数据导致的问题。使用时间戳作为ID确保唯一性,默认值让用户能够快速完成添加操作。

实际应用经验

在开发过程中,我们发现老年用户最关心的是操作简单信息准确。他们不需要复杂的统计图表,但需要清晰的提醒和可靠的记录。

通过多轮用户测试,我们不断简化界面,最终形成了现在这套以大字体、高对比度、简单操作为特色的设计方案。

👥 用户反馈驱动:真正好用的产品都是在用户反馈中不断迭代出来的。我们始终坚持以用户需求为导向,而不是炫耀技术能力。

这套用药管理系统已经在多个养老社区试用,得到了用户的积极反馈。一位78岁的用户说:“现在我再也不担心忘记吃药了,手机会提醒我,我也能看到还剩多少药。”


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐