尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Flutter+OpenHarmony实战:打造家庭药箱管理APP的踩坑与优化

发布时间:2026/9/26 4:44:16

资讯中心
01
ARTICLE

Flutter+OpenHarmony实战:打造家庭药箱管理APP的踩坑与优化

Flutter+OpenHarmony实战:打造家庭药箱管理APP的踩坑与优化
1. 为什么是Flutter OpenHarmony这个组合到底解决了什么问题先交代一下背景。我大概在2023年底拿到了一台基于OpenHarmony的国产开发板当时想给家里做一个药箱管理的小工具用来记录老人每天吃什么药、体温有什么变化。最初想的是直接用ArkTS写个原生应用但后来发现家里的需求变化很快——今天要加一个体温记录曲线明天可能又要加一个药品库存导出用ArkTS从零堆UI效率确实不够理想。于是我把目光转到了Flutter for OpenHarmony这个方向上。坦白说这个方案在圈子里还比较小众但它在逻辑上是成立的OpenHarmony不是Android但它提供了Flutter引擎的移植版本可以让Dart代码直接跑在OpenHarmony设备上。用它来做家庭药箱管理APP最大的价值在于业务逻辑、状态管理、UI布局都可以复用将来如果还想出Android或Windows桌面版本整套Dart代码几乎不用重写。这篇博文不是给你讲Flutter的基础语法也不是只贴一段能跑的代码完事而是把我从搭建环境到完成体温记录、药箱管理核心模块这条路上踩过的坑、想清楚的设计决策都写下来。适合谁看准备在OpenHarmony设备上做实用工具类应用的开发者或者已经有Flutter基础、想低成本切入OpenHarmony生态的人。如果你是零基础文章里涉及的基础概念我也会顺手解释一下不至于看不懂。2. 项目骨架药箱管理APP的整体结构与数据层设计2.1 需求拆解药箱管理不只是记个保质期很多人在做药箱管理应用时容易把它做成一个简单的药品列表日期提醒。但实际用过就会发现家庭药箱的场景比想象中复杂得多同一个药品可能同时被多个家庭成员服用剂量还不一样。药有时放在客厅药箱有时在随身药包库存位置需要区分。体温记录和药品服用最好能关联起来尤其是家里有发烧病人时可以看清吃了退烧药之后体温多久降下来。老人不太会操作复杂界面录体温的操作路径必须短。因此我最终把数据模型拆成了三个核心实体家庭成员Member、药品Medicine、体温记录TemperatureRecord外加一个用药记录UsageLog用来串联成员与药品。2.2 数据库选型为什么不用SharedPreferences硬扛项目初期我考虑过把数据全部塞进SharedPreferences毕竟数据量不大。但很快发现不行——体温记录一天可能测四次一周就是几十条药箱里的药品效期提醒需要按日期做筛选成员之间的数据还要关联查询。用JSON字符串手写这些都太痛苦了。在Flutter for OpenHarmony上我最终选择了OpenHarmony自带的RelationStore关系型数据库通过Flutter的数据库插件来桥接。没有直接用sqflite因为sqflite在OpenHarmony上的适配还不成熟读写经常出问题。数据表结构大致是这样-- 家庭成员表 CREATE TABLE member ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, role TEXT, -- 成员角色老人/成人/儿童 birth_year INTEGER, note TEXT ); -- 药品表 CREATE TABLE medicine ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT, -- 分类感冒/肠胃/慢性病... spec TEXT, -- 规格如 0.25g*24片 expiry_date TEXT, -- YYYY-MM-DD stock INTEGER, location TEXT, -- 存放位置客厅药箱/随身包 reminder_days INTEGER, -- 提前几天提醒 created_at TEXT ); -- 体温记录表 CREATE TABLE temperature_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, member_id INTEGER NOT NULL, temperature REAL NOT NULL, measured_at TEXT NOT NULL, -- ISO时间 source INTEGER DEFAULT 0, -- 0手动录入 1体温计导入 note TEXT, FOREIGN KEY(member_id) REFERENCES member(id) ); -- 用药记录表 CREATE TABLE usage_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, medicine_id INTEGER NOT NULL, member_id INTEGER NOT NULL, take_time TEXT NOT NULL, dosage REAL, note TEXT );提示在OpenHarmony上通过Flutter操作RelationStore前一定要先确认对应插件支持的API版本。我最初按Android习惯直接用sqflite结果在OpenHarmony设备上连数据库文件路径都找不到后来切换到官方的ohos_sqlite适配层才正常。2.3 状态管理用Riverpod还是Provider项目不算大但我还是上了Riverpod。为什么因为药箱管理和体温记录之间有明确的依赖关系当天体温异常时需要推荐对应的用药记录药品库存变化又要刷新首页的效期提醒。Riverpod的watch机制可以很优雅地处理这种跨模块刷新不用写一堆setState回调。我用三个核心Provider来组织状态memberListProvider家庭成员列表任何页面改变成员后其他页面自动监听刷新。medicineListProvider药品列表依赖成员选择。temperatureRecordsProvider体温记录列表按成员和日期过滤。这套结构在后面实现选择成员→查看体温曲线→关联用药记录的联动时几乎没有返工。3. 体温记录功能从数据采集到趋势图表的完整链路3.1 录入体验给测温这个动作做减法家里给老人测体温场景往往是手边有一个电子体温计测完需要立刻记下来。如果打开APP后还要点三四个按钮才能录入老人会直接放弃。所以我把录入页设计成了首页的核心卡片默认带出上一次测温成员和测温时间只需要拖动滑杆或点击数字就能调整温度。这里有一个关键交互决策温度输入不用键盘而是用滑杆预设快捷键。因为体温范围通常在35.5到41.0之间键盘输入小数点很容易误触。// 温度滑杆的核心逻辑简化的示意代码 double _temperature 36.5; Slider( min: 35.0, max: 42.0, divisions: 70, // 0.1度一档 label: _temperature.toStringAsFixed(1), value: _temperature, onChanged: (value) { setState(() { _temperature value; }); }, ) // 快捷按钮一键标记为低热/高热/正常 Wrap( spacing: 8, children: [ ActionChip(label: Text(36.0), onPressed: () _setTemp(36.0)), ActionChip(label: Text(37.0), onPressed: () _setTemp(37.0)), ActionChip(label: Text(37.3), onPressed: () _setTemp(37.3)), ActionChip(label: Text(38.5), onPressed: () _setTemp(38.5)), ], )滑杆的好处是手指粗的人也好操作配合大字体实测老人愿意用。3.2 温度数据的判定与标记体温数据不是存进去就完事了。我在录入时做了两层处理第一层是基础校验35.0以下或42.0以上基本是传感器错误或录入错误直接弹窗拦截。第二层是状态标记enum FeverLevel { normal, // 36.0 - 37.2 lowFever, // 37.3 - 38.0 midFever, // 38.1 - 39.0 highFever,// 39.1以上 lowTemp, // 36.0以下 }这个判定逻辑不仅用于展示还会在首页的成员卡片上给出不同颜色的状态圆点——绿色正常、橙色低热、红色高热。这样家庭成员健康状况一眼就能扫出来。3.3 趋势图表的实现Flutter图表库在OpenHarmony上的适配体温趋势图是整个功能里最有价值的部分因为它能直观反映出退烧药的药效曲线。在Flutter生态里最常用的是fl_chart。但在OpenHarmony上我发现直接引入fl_chart是能编译过渲染也正常不过在真机上拖动时偶尔会有掉帧尤其是同时展示7天数据、每条数据点还有tooltip的时候。后来我做了两个优化数据降采样如果单日测体温次数超过6次图表上只保留最高、最低、最新三个特征点避免数据点过于密集。关闭不必要的动画fl_chart默认的动画在OpenHarmony的低端设备上开销偏高我把duration设置为零。LineChartData( duration: Duration.zero, lineBarsData: [ LineChartBarData( spots: _buildSpots(records), isCurved: true, barWidth: 2, color: _getLineColor(), dotData: FlDotData(show: true), ), ], )实测下来在OpenHarmony开发板上渲染一周的体温数据从页面打开到图表完全呈现大概在200ms以内这个表现是可以接受的。注意如果你用的是较老版本的fl_chart在OpenHarmony上可能遇到PathMetric相关的方法报错。升级到0.60版本后问题基本消失但API有些变化升级时注意LineChartData与FlGridData的参数调整。3.4 体温记录与用药记录的联动单纯画一条曲线其实不难难的是让曲线会说话。我在体温详情页下方加了一个同期用药时间轴把用药记录按时间倒序显示并自动计算最近一次退烧药距离体温上升/下降的时间差。实现思路是点击体温曲线上的某个数据点会弹出一个底部面板显示该时间点前后6小时内的用药记录并标记出哪些药品是退烧/感冒类。比如你在晚上8点测到38.6度面板上就会列出当天18:00之后吃过的药并自动标注对乙酰氨基酚可能已起效体温较2小时前下降0.4度。这个联动逻辑不难但很实用。实际使用中我家老人连续几天体温反复通过这个面板能清楚看到每次测温时的用药情况不用再去翻药盒。4. 药箱管理核心流程效期提醒、库存变动与用药记录4.1 药品录入手输还是扫码在OpenHarmony上的务实选择很多药箱APP都主打扫码录入药品信息。但在OpenHarmony设备上扫码插件的选择非常有限主流Flutter扫码插件大部分依赖Android的CameraX或iOS的AVFoundation在OpenHarmony上要么不可用要么需要额外写原生桥接。我在开发板上试了一圈最终决定输入框中手动录入但做了模糊搜索历史记忆。为什么这样取舍家里的药品就那么多首次录入花点时间之后每次拿药时搜索历史记录几秒钟就能选中。扫码更适合药房场景家庭药箱不是高频变动场景不想为这一功能牺牲整个App的稳定性。录入表单中我特别注意以下几个字段有效期必须精确到日不能只到月。因为儿童药品的效期往往只有半年精确到日能在临界点给出更准确的提醒。库存单位我统一用片/袋/瓶/毫升混合。实际上很简单在药品结构体里加一个unit字段下拉选择。存放位置这个字段很容易被忽略但真实场景中药箱里、床头柜里、冰箱冷藏区里各有不同药品没有位置信息就会出现明明有药却找不到的情况。4.2 效期提醒两个时间窗口缺一不可效期提醒是我个人认为药箱管理中最核心的模块。我设置了两个提醒窗口临期提醒默认提前30天每天登录时弹出横幅蒙脱石散将在15天后过期。过期警告过期当天药品列表直接置灰卡片上出现红色已过期标签并且默认排序中过期药品沉底。这里有个业务细节需要注意过期药品不能一键删除而是要做处理记录。因为家庭药箱里有些药物可能还在服用周期内直接删除会导致用药记录断档。我实现的是封存操作——标记状态为archived不再出现在常规列表中但历史用药记录仍然保留。排序算法也很简单// 按有效期升序排列alreadyExpired的排最后 final sorted medicines.where((m) !m.expired) .toList() ..sort((a, b) a.expiryDate.compareTo(b.expiryDate));4.3 库存联动取药自动扣减还药手动回填家庭药箱最常见的库存变动场景是从药箱拿了一盒药拆了几粒吃掉剩下的放回去。这个过程中如果只是简单做库存-1实际是不准确的。我用的方案是按板/盒为最小取用单位记录剩余粒数。比如一盒头孢有24粒每次取用2粒库存记录从24→22。如果一次取了6粒但只吃了4粒还可以在用药记录里补充剩余2粒已放回库存自动回加。这个逻辑用一套简单的库存事务来完成Futurevoid takeMedicine(Medicine med, int takeCount, int memberId) async { // 先尝试扣减库存 final newStock med.stock - takeCount; if (newStock 0) { // 提示库存不足允许负债但必须加备注 } await _db.updateMedicineStock(med.id, newStock); // 写入用药记录 await _db.insertUsageLog(medicineId: med.id, memberId: memberId, ...); }我故意没有禁止库存变负这个情况原因是家中经常出现药其实还有但没来得及登记的情况。允许负库存但要求用户填写备注反而让系统更灵活。4.4 首页信息密度一个页面看清全家用药状态最终App的首页不是简单罗列而是一张家庭健康看板顶部四位家庭成员的体温概览圆点。中部今日用药计划谁在什么时间吃了什么药是否已完成。底部临期药品横向滑动卡片。这个首页布局的灵感来自车况仪表盘——不需要进入任何二级页面核心信息全部外露。对于家中有老人的场景这比任何复杂的报表都有用。5. OpenHarmony适配实战我踩过的坑与性能调优记录5.1 环境搭建Flutter SDK版本选择是第一道坎在OpenHarmony上跑Flutter最核心的工具不是Android Studio而是DevEco Studio OpenHarmony SDK。我建议直接使用OpenHarmony官方提供的flutter_flutter分支即OpenHarmony-SIG维护的Flutter SDK不要用标准Flutter SDK直接编否则就是无数个undefined symbol的报错。版本匹配需要非常谨慎。我自己试过3.7.12和3.10.x两个版本结论是3.7.x稳定度更高插件兼容性最好但Dart语法较旧。3.10.x一些新版Dart语法特性可以用但个别插件需要自己改兼容。如果你是第一次做建议先用3.7.x跑通一个最小Demo再考虑升级。另外环境变量里配置Flutter SDK路径时不要使用带空格的路径OpenHarmony的构建脚本对空格极其敏感。5.2 文件路径与沙箱权限一个改动就能规避的崩溃在Android上getApplicationDocumentsDirectory()能直接拿到App私有目录但在OpenHarmony上这个路径不一定存在或者没有写权限。我在第一次真机运行时就遇到了Cannot create file的异常。解决方式很简单在初始化时先检查路径存在性并手动创建目录final Directory docsDir await getApplicationDocumentsDirectory(); if (!await docsDir.exists()) { await docsDir.create(recursive: true); }这看起来是个小点但如果你用sqflite来存数据库这个问题会在创建数据库文件时直接导致App闪退连报错信息都不明显。5.3 手势冲突OpenHarmony的侧滑返回与Flutter列表滚动这是我在开发过程中最头疼的问题。OpenHarmony的默认侧滑返回手势是从屏幕左边缘右滑退出页面但我的首页有横向滚动的药品卡片边缘手势和横向滑动经常打架。用户在滑动卡片时经常会误触发返回。解决办法有两个方向在Flutter侧禁用边缘手势支持改为App内自己控制返回逻辑。将横向滚动区域在布局上避开屏幕边缘留出至少20dp的安全距离。我最终选择的是方案2因为它改动最小也不会影响系统级手势的一致性。在具体实现时把横向卡片列表放进一个带padding的容器左右各留出EdgeInsets.only(left: 20, right: 20)。5.4 性能调优List.build与图片占位OpenHarmony开发板我测试的是RK3568方案的板子比主流Android手机性能弱不少。我做了两个很实际的优化所有列表都用.build方法构建Item不用ListView(children: ...)。当药品数量超过50条时两种写法的首帧渲染时间差异能到300ms以上。图片全部走本地资源并使用cacheWidth强制降采样。药品照片如果直接用原图在低端设备上会引发明显的列表卡顿。Image.file( File(medicine.photoPath), cacheWidth: 360, cacheHeight: 240, fit: BoxFit.cover, )这个cacheWidth参数很多Flutter开发者在做Android时都不常用但在OpenHarmony这种资源受限的平台上非常关键。它能显著降低内存占用避免OOM。实测我的项目里48张药品图片加上GPU渲染后内存在150MB以内稳定运行。5.5 通知提醒不要依赖插件自己走OpenHarmony通道在Android上做定时提醒通常用flutter_local_notifications。但在OpenHarmony上这个插件的适配尚不成熟。我一开始试图引入它结果在平台通道调用时直接崩了。后来我改成了在OpenHarmony原生侧实现通知推送通过Flutter的MethodChannel来调用。大致流程是Flutter侧每天早上的固定时间点检查数据库中临期药品列表。如果有临期药品通过MethodChannel调用原生侧的ohos_notification接口。原生侧发送一条带标题和内容的系统通知。这个方法没有用任何第三方Flutter插件完全基于OpenHarmony自己的通知能力反而最稳定。如果你也需要在Flutter for OpenHarmony里做提醒强烈建议直接走这个方法不要在插件市场里浪费时间。6. 从能跑到好用离线优先与交付体验6.1 离线优先设备没网也能正常工作家庭药箱管理有一个被很多人忽视的前提它应该在完全没有网络的环境下也能完整使用。很多智能家居App都需要登录账号、同步云端一旦Wi-Fi断了就变成摆设。而药箱管理发生在家里、厨房、卧室这些地方网络状况不一定好。我的做法是把本机作为唯一数据源所有数据都先写本地数据库。所谓云端同步只是提供了一个导出备份和跨设备导入的能力导出把数据库序列化成JSON文件通过分享功能保存到本地或发送到其他设备。导入读取JSON文件全量覆盖本地数据带确认弹窗。这个设计极大简化了开发工作量也符合家庭使用场景——大多数家庭只有一台设备承担药箱管理职责根本不需要实时的多端同步。如果你想要多设备同步建议后续接入一个简单的文件同步工具比如WebDAV而不是自己搭服务端性价比要高得多。6.2 真机使用后的设置项让App越用越顺手在调通所有功能并给家人实际使用两周后我根据反馈增加了三个设置项虽然都不复杂但体验提升非常明显体温单位切换摄氏/华氏一次设置全局生效。提醒时间点默认早上8点检查效期但可以自定义避免打扰作息。成员排序家中有多人时经常需要把老人固定在第一位方便测温后迅速录入。这些小设置在一定程度上决定了工具类App的留存率。功能再多不好用也没用。尤其是家庭成员记录体温这个场景操作路径短、反馈明确、信息直观比堆功能重要得多。6.3 我实际使用中的几点体会项目从开始到能稳定给家人用前后花了大概三周时间其中环境适配和插件踩坑占了一半。回头看几个选择被证明是正确的数据层从第一天就用关系型数据库后期加功能没有返工。界面以大字号大点击区域为第一原则家里老人在没有我指导的情况下自己就能完成体温录入和查看提醒。没有强行上扫码、云同步等花哨功能让App保持在一个打开就懂的复杂度。另外还有一个细节所有弹窗按钮的文案我都避免使用确定删除这类字眼改成继续处理移入封存这种更符合上下文的行为描述。对老年人来说他们面对抽象按钮时经常犹豫不决而写明动作的按钮能大幅减少误操作。这是我做完这个项目之后比较深的感触工具类应用的价值不在于功能多而在于在关键时刻它能比人脑更快、更准确地提醒你该做什么。OpenHarmony生态还在成长Flutter给这个生态带来的跨平台开发能力让个人开发者可以用很低的成本做出真正提升生活质量的设备端应用。这一套组合拳未来的空间还很大。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。