Flutter在OpenHarmony上实现家庭药箱管理App的实战解析

发布时间:2026/10/6 9:06:40
Flutter在OpenHarmony上实现家庭药箱管理App的实战解析 家里常备药越堆越多有的过期了还躺在角落里老人每天吃什么药没人提醒血压测完记在纸上转头就找不到了——这些问题我相信每个家庭都遇到过。我这次用 Flutter 在 OpenHarmony 上完整实现了一个“家庭药箱管理 App”把药品建档、过期预警、用药提醒和血压记录全部做了进去。整个过程踩了不少坑尤其是 Flutter 组件通信、OpenHarmony 原生能力桥接、还有 Flutter 在 OpenHarmony 上的渲染引擎适配问题今天一次性把这些实战细节都整理出来给准备做 OpenHarmony 应用开发的朋友一份能直接参考的方案。这个项目适合谁看如果你已经会 Flutter 基础想了解一下 Flutter 在 OpenHarmony 上到底能不能打或者你准备开发一个 OpenHarmony 应用但是不想从 ArkTS 零开始写 UI又或者你想看一个“药品数据管理 健康数据记录”这类典型业务在非 Android/iOS 平台上怎么落地——这篇文章都会对你有用。1. 项目方案设计与技术选型1.1 为什么选 Flutter 做 OpenHarmony 应用先回答一个很多人纠结的问题OpenHarmony 官方推荐的是 ArkTS ArkUI为什么我还要用 Flutter我的判断是团队里如果已经有 Flutter 的技术积累尤其是那些已经在 Android 和 iOS 上跑过 Flutter 业务的团队切到 OpenHarmony 的成本其实很低。Flutter 的渲染不依赖系统原生控件它自己有一套自绘引擎所以在 OpenHarmony 上跑起来之后UI 表现和 Android 端几乎一致不用为不同平台去调原生 UI。这对家庭药箱管理这种表单多、列表多、图表多的应用来说非常省事。另外一个现实因素是 OpenHarmony 生态已经有 Flutter 官方适配了。OpenHarmony SIG 在 Gitee 上维护了 flutter_flutter 的 ohos 分支配合 DevEco Studio 使用Flutter 的工程可以直接构建出 OpenHarmony 应用包。也就是说你写一套 Dart 代码理论上 Android、iOS、OpenHarmony 三端都能跑。家庭药箱这类小中型工具正好适合用这种跨端方案快速落地。不过你要有心理准备Flutter on OpenHarmony 的插件生态还没有 Android/iOS 那么成熟。很多 pub.dev 上的插件没有直接适配 OpenHarmony底层依赖原生能力的插件需要你手动找 ohos 版本或者自己用 MethodChannel 封装。这一点我在后面会详细讲。1.2 功能拆解家庭药箱管理到底要解决什么问题动手写代码之前先把业务需求理清楚。家庭药箱管理不是简单的“记一下药名”我拆成了四个核心模块药品档案管理录入药品名称、类别处方药/OTC/保健品、规格、库存数量、生产批号、有效期、存储条件、服用方法和当前存放位置。这里的关键是有效期和批号因为药品不同于普通商品过期处理涉及到安全风险不能只记一个大概日期。过期预警药品入库时记录有效期系统每天计算剩余天数把药品分成“已过期”“30天内即将过期”“正常”三个级别。这个用查询时的时间差计算就能实现不需要搞后台定时任务。用药提醒按药品设置每日服药时间和剂量。这里有个细节——App 退到后台之后纯 Dart 的 Timer 是不可靠的需要走 OpenHarmony 系统的提醒代理服务ReminderAgentManager来发通知。我在项目里封装了一个 PlatformService 通道来处理。血压记录用户录入收缩压、舒张压、心率、测量时间和备注支持按周/月查看趋势折线图。这是典型的健康数据校验逻辑不能马虎收缩压必须大于舒张压数值范围也要限制。这四个模块覆盖了“药品生命周期管理”和“健康数据跟踪”两条主线既有常规的 CRUD 操作又有系统开放的提醒能力和图表渲染对 Flutter 开发者来说这是一个能把跨端开发核心能力全部练一遍的项目。1.3 项目架构状态管理、数据层与目录规划架构上我没有引入特别重的框架用的是 Provider Repository 模式。原因很简单这个项目的数据流不算复杂药品列表和血压记录是两个相对独立的领域模型用 Provider 管理 UI 状态完全够用Riverpod 或 Bloc 在这里属于过度设计。数据持久化我选了 sqflite 适配 OpenHarmony 的实现。OpenHarmony 自带 SQLite 能力社区有一个兼容层的插件包底层还是走 SQLite 原生驱动。流式查询和事务都支持适合存放结构化数据。药品的图片我用文件路径保存不丢数据库避免数据库膨胀。目录结构我按 feature-first 来组织lib/ ├── core/ │ ├── constants/ # 常量、枚举、颜色、文案 │ ├── theme/ # 全局主题 │ ├── utils/ # 日期处理、血压分级判断、过期计算 │ └── platform/ # OpenHarmony 原生通道封装 ├── data/ │ ├── models/ # 药品、血压记录、提醒的模型类 │ ├── repositories/ # 药品仓库、血压仓库、提醒仓库 │ └── database/ # 数据库表结构、DAO ├── providers/ # Provider 状态管理 ├── features/ │ ├── medicine/ # 药品列表、添加/编辑、详情、预警 │ ├── blood_pressure/ # 血压录入、趋势图表、统计分析 │ └── reminder/ # 用药提醒设置与管理 └── app.dart # 应用入口与路由Feature 内部分成 pages、widgets、widgets 三个子目录和页面强相关的小组件就放在本 feature 下不搞全局 widgets 目录否则后期维护会疯掉。2. OpenHarmony 开发环境搭建与项目初始化2.1 环境准备SDK 下载与配置Flutter for OpenHarmony 的开发环境核心是两套东西OpenHarmony SDK通过 DevEco Studio 管理和 Flutter ohos 分支 SDK。我实际用下来的配置步骤是这样的安装 DevEco Studio版本建议 4.0 以上它自带的 SDK Manager 可以下载 OpenHarmony SDK包含 API 10/12 等不同版本。下载 Flutter ohos 分支 SDK。打开终端执行git clone -b ohos https://gitee.com/openharmony-sig/flutter_flutter.git把仓库克隆到你打算放 Flutter SDK 的位置。配置环境变量。至少需要设置OHOS_SDK_HOME指向 DevEco Studio 下载的 OpenHarmony SDK 目录注意不是 DevEco Studio 安装目录是 SDK 目录一般长这样/opt/DevEcoStudio/sdk或C:\Users\xxx\DevEcoStudio\sdk。把 flutter 的 bin 目录加入 PATH在终端里验证一下flutter doctor如果能识别出OpenHarmony相关的环境项说明配置没问题。注意flutter doctor在 ohos 分支下可能会显示一些和 Android/iOS 无关的警告只要能看到 OpenHarmony toolchain 是 OK 的状态就可以继续。千万不要拿官方的稳定版 Flutter SDK 来跑 OpenHarmony 工程编译时会直接报找不到 OHOS 平台的错误。2.2 创建 Flutter for OpenHarmony 项目创建项目的时候不要用默认的flutter create不带平台参数那样只会生成 Android 和 iOS 目录。ohos 分支的 Flutter 工具链支持显式声明平台flutter create --platforms ohos --project-name family_medicine_kit --org com.example ./执行完你会看到工程里多了一个ohos目录里面是 OpenHarmony 原生工程的骨架。这个目录结构对应的是 OpenHarmony 的 HAP 工程格式有entry/src/main之类的子目录。第一次运行之前要检查两件事ohos/entry/src/main/module.json5文件里的 bundleName 要和后面签名时用的包名一致否则装不进真机。OpenHarmony 真机调试必须要签名。DevEco Studio 里有一个“Project Structure Signing Configs”的引导流程会自动生成 p12、cer、p7b 三个文件。自动签名需要登录华为账号但是和 OpenHarmony 社区版是可以兼容的。如果只是模拟器调试可以跳过签名。2.3 真机运行与调试配置连接 OpenHarmony 真机比如开发板或者安装了 OpenHarmony 的手机后在工程根目录执行flutter devices正常情况下能看到类似OHOS device xxx的条目。如果设备列表里看不到排查两步确认 USB 调试模式已打开在设置里打开“开发者选项”开启 USB 调试确认 DevEco Studio 的 hdc 工具链配置正确。hdc 等价于 Android 的 adb路径一般在 DevEco Studio 的 toolchains 目录里。能识别设备后直接运行flutter run -d 设备ID第一次构建时间会比较长因为它要同时编译 Flutter 的 C 引擎和 OpenHarmony 原生工程。等到命令行出现Flutter run key commands的提示说明已经跑起来了热重载按r就能生效实测在 OpenHarmony 上热重载响应速度和 Android 差不多。3. 家庭药箱管理核心模块实现3.1 药品建档数据模型与表单校验药品建档是整个应用的数据入口模型我这样设计class Medicine { final int? id; final String name; final String category; // 处方药、OTC、保健品等 final String specification; // 规格比如 0.25g*24片 final int stockCount; final String batchNumber; final DateTime? expiryDate; final String storageCondition; // 常温/阴凉/冷藏 final String dosageGuide; // 服用方法比如 每日两次每次一片 final String location; // 存放位置比如 客厅药箱第二层 final String? photoPath; }表单页用 Flutter 的FormTextFormField做校验规则我重点踩了几个细节。有效期必须选未来的日期否则入库当天就变成“已过期”但实际上很多家庭药箱里确实有过期药这个时候应该允许录入但要有明确提示。库存数量不能是非数字也不能为负数用数字键盘 正则兜底。批号不是必填但建议用户填上后面如果遇到药品召回通知批号是关键的追溯信息。存储条件这里我用了枚举下拉常温、阴凉不超过20度、冷藏2-8度、避光。这个字段虽然简单但对药效保持很重要而且很多用户并不知道药品储存讲究这些界面里给了说明文案顺手科普。3.2 过期预警算法与提醒列表过期预警不需要后台任务数据库查询时实时计算就可以。我对每个药品拿到expiryDate之后计算expiryDate.difference(DateTime.now()).inDays然后分级状态判定条件UI 表现正常剩余天数 30绿色标签临期剩余天数在 1~30 天之间橙色标签标注XX天后过期已过期剩余天数 0红色标签标注已过期XX天不允许设为正常状态列表按“已过期优先、临期次之、正常最后”排序再用 Provider 监听药品表变化每次增删改后自动刷新。这里有一个我踩过的坑日期比较不要直接用expiryDate的时分秒去减DateTime.now()因为会有零点时间差导致少算一天。正确做法是先把两个日期都归一到零点DateTime normalize(DateTime d) DateTime(d.year, d.month, d.day); int daysUntilExpiry normalize(expiryDate).difference(normalize(DateTime.now())).inDays;3.3 搜索、分类与药品详情药品搜索我用 SQLite 的LIKE查询对名称、类别、存放位置三个字段做模糊匹配。有基础中文搜索的场景LIKE够用了不用上全文索引。分类统计则是在列表页顶部放一排 FilterChip按“全部”“处方药”“OTC”“保健品”“外用药”做过滤。因为我用 Provider 管理药品列表搜索词和选中分类都会作为ListMedicine get filteredMedicines的依赖内部做内存过滤。药品数量到几百条级别内存过滤完全无压力反而比频繁查数据库更流畅。药品详情页做了三个核心操作区药品基础信息卡片、服用说明与注意事项、操作按钮编辑、删除、设置提醒。删除药品要二次确认弹窗并提示“删除后将同时移除该药品的提醒计划”避免用户误删后收不到提醒。4. 血压记录模块实战4.1 血压录入表单从校验到生活化引导血压记录表面看就是一个简单表单但它比药品录入更需要细心。医学上有明确的血压分类标准数据录歪了会误导用户。我做的表单字段是收缩压高压、舒张压低压、心率、测量日期、测量时间、备注可选。校验规则三条收缩压范围 70~250 mmHg舒张压范围 40~150 mmHg。收缩压必须大于舒张压否则弹提示“收缩压应高于舒张压请检查是否输入颠倒”。心率范围 40~200 次/分低于或高于这个区间会提醒“建议复测或就医”。录入完成后系统根据《中国高血压防治指南》的血压分级标准在结果页给用户一个直观的状态提示。这个功能虽然逻辑简单但用户反馈很好很多人会发现自己已经处于“正常高值”状态比单纯记录数字有意义得多。提示血压测量有很多干扰因素我特意在录入页加了一句引导文案“请在安静状态下休息5分钟后测量测量前30分钟避免吸烟、喝咖啡或剧烈运动。”这既是对用户的健康负责也减少了多次测量数值波动导致的误判。4.2 血压趋势图fl_chart 在 OpenHarmony 上的表现趋势图我用 fl_chart 库来做。一开始有点担心它有没有适配 OpenHarmony实际跑下来发现它是纯 Dart 实现的图表库不依赖任何原生控件所以 OpenHarmony 上直接能用数据点渲染流畅度没问题。我实现了两个维度近7天/30天趋势折线图X 轴是日期Y 轴是血压值画两条折线一条收缩压、一条舒张压再加一条 140/90 的参考警戒线。平均血压统计卡片把当前时间范围内的收缩压、舒张压分别取平均标注最高值和最低值出现日期。这里有一个经验Y 轴的刻度范围不要从 0 开始否则 60~180 的数据会被压得很扁看不清楚波动。我把 Y 轴下限设为收缩压/舒张压最小值向下取整到 10 的倍数再减 10上限同理加 10这样折线的起伏会明显很多。4.3 数据持久化与导出血压记录表结构是id, systolic, diastolic, heart_rate, measured_at, note。measured_at存毫秒时间戳方便按时间范围做聚合查询。导出功能我实现了“生成 CSV 并保存到本地”用 path_provider 的适配版获取应用文件目录把 CSV 写到文件里同时提供分享入口。CSV 的格式我特意做了中文表头这样长辈打开文件也能看懂序号,测量时间,收缩压(mmHg),舒张压(mmHg),心率(次/分),备注 1,2025-05-20 08:30,138,86,72,晨起服药前导出 CSV 之前先查一遍有没有;、换行符这些危险字符备注字段里如果有换行会导致列错位要做转义或者替换。这个坑我在自测时遇到过后来加了field.replaceAll(RegExp(r[\r\n,;]), )来兜底。5. Flutter 与 OpenHarmony 原生能力的桥接落地5.1 组件通信从父子传值到全局状态Flutter 组件通信这个点是 OpenHarmony 开发里绕不开的因为 Flutter 的组件树通信方式和 ArkTS 不一样。我在项目里实际用到了三种通信方式父传子构造函数直接传参。比如药品列表页把筛选条件对象传给子组件这是最简单直接的方式。子传父通过回调函数。比如血压趋势图组件把“用户点击了某个数据点”的事件通过onPointTap回调传给父页面显示对应的测量详情。跨组件/跨页面共享状态用ChangeNotifierProvider。药品数据、血压数据、提醒列表都是全局状态任何页面修改后依赖这些状态的组件自动重建。比如在药品详情页编辑库存数量回到列表页不需要手动刷新数据自动就是最新的。还要提一个容易踩坑的细节在 Flutter 里如果用async函数Future的.then回调在 Dart 的微任务队列里执行而不是立即同步执行。如果你在then里访问 Provider 的 state 并且依赖了某个页面生命周期状态要注意执行顺序否则可能拿到旧数据。我当时的解法是尽量用await写线性代码而不是嵌套多个.then代码可读性和执行顺序都清晰很多。5.2 用 PlatformView 渲染 OpenHarmony 原生组件药品详情里我放了一个“用药提醒设置”卡片其中时间选择器用了 OpenHarmony 原生的时间选择控件这样体验上更贴近系统风格。这就用到了 Flutter 的 PlatformView 机制。在 OpenHarmony 适配层中Flutter 引擎提供了PlatformView的注册方法原生侧对应是用ArkTS组件包一层然后把视图 ID 传给 Flutter 侧。这个流程比 Android 上的 PlatformView 要简单一点但是要注意PlatformView 的创建是异步的Flutter 侧拿到的 controller 可能需要等视图创建完成才能交互如果不用onPlatformViewCreated回调做二次初始化会出现原生控件渲染出来了但点击没反应的问题。5.3 系统提醒能力与权限声明用药提醒是整个应用里对原生依赖最深的功能。Flutter 层只能用 Timer 做应用在前台时的提醒App 一旦切后台Dart 的 Timer 大概率被系统挂起。所以我把通知提醒封装成原生侧功能用 OpenHarmony 的 ReminderAgentManager 系统能力。具体做法是写一个MethodChannel(family_medicine/reminder)Flutter 侧调用原生方法final result await _channel.invokeMethod(addReminder, { title: medicine.name, content: 服药时间到${medicine.dosageGuide}, hour: hour, minute: minute, repeatDays: repeatDays, id: reminderId, });原生侧ArkTS收到调用后通过 reminderAgentManager 创建定时提醒。这里必须检查通知权限OpenHarmony 应用需要用户授予通知发送权限在module.json5里声明requestPermissions: [ { name: ohos.permission.NOTIFICATION_CONTROLLER, reason: 需要发送用药提醒通知 } ]权限没有正确声明或者用户没有授权时提醒不会弹出但代码不会报错排查起来很容易忽略。另外提醒删除不能依赖 Flutter 侧的局部状态。如果用户删除了药品提醒仍然会在原生侧触发需要在deleteMedicine的逻辑里同步调用 MethodChannel 的removeReminder。这个联动我在初版漏掉了一次结果删了药品之后到了点还是收到吃药通知非常尴尬。6. 常见运行问题与排查技巧实录6.1 经典报错 e/flutter Dart VM 初始化失败开发阶段我在 OpenHarmony 真机上跑起来遇到了一类高频报错日志长这样e/flutter (31173): [error:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled exception这个报错本身只是一个统称真正有用的信息在它下面一条。常见的根因有几个空安全相关的 null 检查失败比如从数据库里读取一个可空字段后直接用了插件通道调用的原生方法返回了 null但 Flutter 侧声明成非空类型还有网络请求解析 JSON 时类型不匹配。我排查这类问题的方式是先把日志完整展开找到 Unhandled exception 下面的第一行#0堆栈配合--verbose参数重新运行flutter run -d 设备ID --verbose大多数情况都能在堆栈里定位到具体是哪个 repository 方法出了问题修复之后再热重启。建议把日志级别调到 debugAndroid 上可能直接看adb logcatOpenHarmony 上就用 hdc 的日志命令hdc hilog流程是相通的。6.2 Impeller 渲染引擎的兼容性处理OpenHarmony 上跑 Flutter默认渲染引擎走 Impeller 的话一部分设备会出现文字模糊、自定义 shader 不生效的情况。Impeller 在 OpenHarmony 的适配进度还没有达到 Android/iOS 那么成熟我在开发板上遇到过一次启动黑屏后来换成 Skia 引擎就正常了。切换方式不需要改代码在ohos/entry的配置文件里设置一个渲染引擎标记或者在flutter run命令后加启动参数flutter run --no-enable-impeller我在项目里是直接配置成 Skia 为主等 Impeller 在 OpenHarmony 上稳定后再改回来。如果你在 OpenHarmony 上做图表、富文本或者视频渲染建议提前用目标设备实测一遍对比一下两种引擎的表现差异。6.3 Flutter Gradle 插件与依赖集成的坑OpenHarmony 工程的依赖管理方式和 Android 类似也会走 Gradle 构建。有一个高频报错是You are applying Flutters main Gradle plugin imperatively using the apply script method这个报错的意思是你在settings.gradle或者build.gradle里用apply from:或者apply plugin:的方式加载了 Flutter 的 Gradle 插件但新版本的 Flutter 插件机制要求改用插件声明式引用。解决方法是修改settings.gradle把原来的apply方式替换成plugins块以内的声明plugins { id dev.flutter.flutter-plugin-loader version 1.0.0 }同时确认项目根目录的settings.gradle里已经声明了 flutter sdk 路径。这类问题本质上不是 OpenHarmony 特有的而是 Flutter 版本升级后项目模板没跟上导致的遇到直接按插件的 README 或者报错提示修正即可。6.4 数据查询与 UI 状态不一致最后分享一个业务逻辑层面的坑。药品列表页用的是 Provider 的Consumer监听状态变化但是删除药品后列表没有刷新排查发现是 Repository 的删除方法虽然写了await db.delete(...)但调用处忘记notifyListeners()。这类“改库不通知”的问题在 Flutter 状态管理里非常典型和 OpenHarmony 本身没有关系但当你把状态管理的逻辑和平台通道混在一起时很容易漏掉。我的经验是尽量把数据操作收敛到 Repository 层所有会改变数据库的公开方法都返回Futurevoid然后在 Provider 层统一调用notifyListeners()而不是在页面里散弹式调用。这样排查数据一致性问题的时候只需要看 Provider 里的几个方法。回到项目本身Flutter for OpenHarmony 现在已经不是“能不能跑”的阶段而是“企业愿不愿意迁移”的阶段。家庭药箱管理这种业务复杂度适中的项目正是验证这套技术栈的好场景。我个人做完这个项目的体会是如果团队有 Flutter 基础OpenHarmony 应用完全可以优先考虑 Flutter但一定要在项目启动前就把插件适配清单列出来凡是涉及系统级能力的功能提前在 OpenHarmony 真机上验证一遍不要到联调阶段才发现某个关键依赖没有 ohos 版本那就相当被动了。