Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 flutter_doc_scanner
文档扫描看起来像一个“调用摄像头再返回文件”的小功能,实际上 Android 和 iOS 的实现依赖了不同的系统能力:Android 侧调用 ML Kit Document Scanner,iOS 侧调用 VisionKit。把 flutter_doc_scanner 带到鸿蒙时,最容易犯的错误是把 HAP 构建成功误认为扫描能力已经完成。
本文以 flutter_doc_scanner 0.0.21 为例,记录我如何保留原有 Dart API、补齐 Flutter OH 插件入口、让鸿蒙端明确返回能力边界,并在 HarmonyOS API 26 真机上完成构建、安装、启动和按钮调用验证。当前分支可以运行 Demo,但鸿蒙系统扫描器 UI 尚未接入,所以真机最终显示 UNSUPPORTED。这个结果是适配过程的一部分,也是比返回空结果更可靠的结论。
适配仓库: oh-flutter/flutter_doc_scanner
适配分支: feat/ohos_flutter_doc_scanner_0.0.21
适配 TAG: 0.0.21-ohos.1
一、先看最终效果
图 1:HarmonyOS API 26 真机启动 Demo。页面显示插件返回的 OpenHarmony 3.47.4,状态为等待操作。
图 2:点击“扫描文档”后的真实结果。ArkTS 返回 UNSUPPORTED,Dart 层把错误码和原因显示在页面上。
| 验证点 | 实测结果 | 证据 |
|---|---|---|
| Flutter OH 3.47 工程构建 | 通过 | entry-default-signed.hap |
| 签名、安装和启动 | 通过 | 图 1 |
getPlatformVersion | 返回 OpenHarmony 3.47.4 | 图 1 |
getScanDocuments | 返回 UNSUPPORTED | 图 2 |
| 其他三个扫描方法 | 已注册,返回明确不支持错误 | ArkTS 分支 |
| 鸿蒙扫描器 UI | 尚未接入 | 适配边界 |
为了让每个入口都可以单独复现,我又在真机上依次点击了图片、PDF 和 URI 三个按钮:
图 3:调用 getScannedDocumentAsImages 后返回同一套 UNSUPPORTED 语义。
图 4:调用 getScannedDocumentAsPdf 后没有伪造 PDF 路径。
图 5:Android 专属的 getScanDocumentsUri 在鸿蒙端也会明确告知当前不可用。
图 6:图片入口返回后,页面最终显示错误码和原因,证明调用链已经回到 Dart 层。
二、这个库原来是怎么工作的
上游包的公共入口是 FlutterDocScanner。它提供四类调用:默认扫描、返回图片、返回 PDF 和返回 Android URI。Dart 层通过名为 flutter_doc_scanner 的 MethodChannel 把方法和参数传给原生端。
Dart FlutterDocScanner
-> MethodChannel('flutter_doc_scanner')
-> Android ML Kit / iOS VisionKit
-> 图片路径、PDF 路径或 URI
本次适配先保留 Dart API 和通道名称,再新增 OHOS 插件实现。OHOS 端没有把 Android 的 ML Kit 或 iOS 的 VisionKit 直接搬过来,也没有伪造一个空的扫描结果,而是把当前能力状态通过标准错误返回给 Dart 层。
三、实际环境
Flutter 鸿蒙环境搭建不在本文重复展开,直接参考 Flutter OH 环境搭建指南。
| 组件 | 版本 |
|---|---|
| Flutter OH | 3.47.4-ohos-1.0.4 |
| Dart | 3.13.3 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26 |
| 测试设备 | HarmonyOS 7.0.0.105,API 26 |
| 目标库 | flutter_doc_scanner 0.0.21 |
flutter --version
hdc list targets
四、确认上游版本并同步到 AtomGit
上游包地址是 GitHub shirsh94/flutter_doc_scanner,版本 0.0.21 使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现,因此从上游基线开始增加平台目录。
仓库初始适配提交为 3360835,完成后分支最新提交为 b5efbd9。
git clone https://atomgit.com/oh-flutter/flutter_doc_scanner.git
cd flutter_doc_scanner
git log --oneline -5
git switch feat/ohos_flutter_doc_scanner_0.0.21
git remote -v
先确认仓库、版本和远程地址,再开始改代码,后面出现编译问题时才容易区分是上游问题、工具链问题还是本次改动引入的问题。
五、创建适配分支并检查工程结构
git switch -c feat/ohos_flutter_doc_scanner_0.0.21
git branch --show-current
关键目录如下:
flutter_doc_scanner/
├── lib/ # 原有 Dart API 和 MethodChannel
├── ohos/
│ ├── index.ets # OHOS 插件导出入口
│ └── src/main/ets/components/plugin/
│ └── FlutterDocScannerPlugin.ets # MethodChannel 实现
├── example/lib/main.dart # 真机验证 Demo
├── example/ohos/ # Flutter OH 宿主工程
├── pubspec.yaml # 声明 ohos 平台
└── README.OpenHarmony_CN.md # 鸿蒙适配说明
pubspec.yaml 中的关键声明是:
flutter:
plugin:
platforms:
ohos:
pluginClass: FlutterDocScannerPlugin
pluginClass 必须和 ohos/index.ets 导出的类名一致,否则 Dart 端能编译,运行时却不会注册插件。
六、适配过程:先对齐契约,再写 ArkTS
我先从 lib/flutter_doc_scanner.dart 和 lib/flutter_doc_scanner_method_channel.dart 读取现有接口,没有改动公开方法名和参数:
| Dart 方法 | 参数 | OHOS 本次策略 |
|---|---|---|
getPlatformVersion | 无 | 返回 OpenHarmony 3.47.4 |
getScanDocuments | page | 返回 UNSUPPORTED |
getScannedDocumentAsImages | 页数、格式、质量、单图模式 | 返回 UNSUPPORTED |
getScannedDocumentAsPdf | page | 返回 UNSUPPORTED |
getScanDocumentsUri | page | 返回 UNSUPPORTED |
这里有一个容易忽略的区别:null 是“调用完成但没有结果”的正常数据形态,常用于用户取消扫描;UNSUPPORTED 是“当前平台没有这项能力”的错误形态。两者混用会让业务层把“鸿蒙尚未适配”误认为“用户取消”。
OHOS 插件骨架使用 Flutter OH 的 FlutterPlugin、MethodCallHandler 和 MethodChannel:
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel?.setMethodCallHandler(null);
this.channel = new MethodChannel(
binding.getBinaryMessenger(),
'flutter_doc_scanner',
);
this.channel.setMethodCallHandler(this);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.channel?.setMethodCallHandler(null);
this.channel = null;
}
注册时使用的通道字符串必须和 Dart 层完全相同。解绑时清理 handler,避免引擎重复创建后出现重复回调。
核心方法分支如下:
case 'getScanDocuments':
case 'getScannedDocumentAsImages':
case 'getScannedDocumentAsPdf':
case 'getScanDocumentsUri':
result.error(
'UNSUPPORTED',
'Document scanning is not available on OpenHarmony yet.',
null,
);
return;
result.error 会被 Dart MethodChannel 捕获为 PlatformException,插件 Dart 实现再把它转换成 DocScanException。Demo 只捕获 DocScanException 并展示 code 和 message,所以真机页面能看到 UNSUPPORTED,而不是一串难以判断的空值。
七、解决两个构建配置问题
第一次构建时,Hvigor 报工程 compatibleSdkVersion 为 18,但依赖的 @ohos/flutter_ohos 要求最低 24。示例工程改成 API 26 后继续构建:
{
"name": "default",
"compatibleSdkVersion": "26.0.0",
"targetSdkVersion": "26.0.0",
"runtimeOS": "HarmonyOS"
}
第二个错误是本机调试签名中的 bundleName 与示例的 com.example.flutter_doc_scanner_example 不一致。为了使用本机 DevEco 调试签名,我把 Demo 的 AppScope/app.json5 统一成 com.example.flutter_oh_demo,并用这个包名启动。证书、p12、p7b 和密码没有提交到仓库,读者应在自己的 DevEco Studio 中生成调试签名。
八、通过 AtomGit 引入适配版
在另一个 Flutter OH 应用中使用时,建议锁定 TAG:
dependencies:
flutter_doc_scanner:
git:
url: https://atomgit.com/oh-flutter/flutter_doc_scanner.git
ref: 0.0.21-ohos.1
flutter pub get
然后在 pubspec.lock 检查 source: git、AtomGit 地址和 resolved-ref,确认没有解析到 pub.dev 的同名版本。
九、构建、安装和真机验证
cd example
flutter pub get
flutter analyze
flutter build hap --debug
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo
构建输出为 ✓ Built build/ohos/hap/entry-default-signed.hap.,随后 hdc 报告安装成功并启动 Ability。进入页面后先看到图 1,再点击“扫描文档”,得到图 2。
Dart 单元测试在本机还受到 Flutter SDK 缓存混用影响,测试进程报告 Invalid kernel binary format version (expected 130, found 138),没有产生有效测试结果。 flutter analyze 可以完成,只有上游 Web 实现使用 dart:html 的弃用提示。这个错误属于测试运行时缓存与 SDK 不一致,不能当作 ArkTS 适配失败;在全新、只使用 Flutter OH 3.47.4 的环境中应重新执行测试。
十、适配结果和当前边界
| 层次 | 已完成内容 | 尚未完成内容 |
|---|---|---|
| Dart API | 保留原方法、模型和 DocScanException | 无 |
| MethodChannel | 通道名和方法名全部对齐 | 无 |
| OHOS 插件 | 注册、版本查询、错误回传 | 真正的扫描 UI |
| Demo | API 26 真机启动和按钮调用 | 拍照、裁剪、生成图片/PDF |
| 文档 | README.OpenHarmony_CN.md、CHANGELOG | 无 |
Android 的 ML Kit 和 iOS 的 VisionKit 不能通过改一个平台声明就变成鸿蒙能力。下一步如果要完成真正扫描,需要选定 HarmonyOS 的相机、图像处理和文件导出 API,补充相机权限,处理前后台切换和用户取消,并为图片、PDF、URI 三种返回结果分别做真机回归。当前提交明确告诉使用者“这个能力还没有接入”,比让业务误以为扫描成功更容易维护。
十一、FAQ
Q1:为什么 HAP 构建成功,页面仍然显示不支持?
- 现象: HAP 可以签名、安装并启动,但点击扫描后显示
UNSUPPORTED。 - 原因: 当前 OHOS ArkTS 端只完成插件注册和错误语义,没有接入系统扫描器 UI。
- 解决方法: 保留明确错误,后续接入真正的 HarmonyOS 扫描实现后再替换对应分支。
- 验证结果: 图 2 证明错误从 ArkTS 经 MethodChannel 到达 Dart 页面。
Q2:为什么不能直接返回 null?
- 现象: 如果 OHOS 端返回
null,Dart 业务很容易把它当成用户取消。 - 原因:
null是一次合法调用没有结果,UNSUPPORTED才是能力不存在。 - 解决方法: 四个扫描方法统一调用
result.error('UNSUPPORTED', ...)。 - 验证结果: Demo 显示错误码和英文原因,状态可追踪。
Q3:为什么第一次构建提示 compatibleSdkVersion 18?
- 现象: Hvigor 报
@ohos/flutter_ohos要求最低兼容版本 24。 - 原因: 示例工程沿用了旧的 18 配置。
- 解决方法: 将示例产品的 compatible 和 target SDK 都改为 26。
- 验证结果: 重新执行
flutter build hap --debug通过。
Q4:为什么签名会报 bundleName 不一致?
- 现象:
SignHap报 generated SigningConfigs 与app.json5的 bundleName 不匹配。 - 原因: 本机调试签名绑定了
com.example.flutter_oh_demo,示例仍是另一个包名。 - 解决方法: 使用自己的 DevEco 调试签名,并让本地
AppScope/app.json5与签名对应。 - 验证结果: 签名 HAP 生成、安装和启动均通过。
十二、总结
这次适配保留了 flutter_doc_scanner 0.0.21 原有 Dart 接口和 flutter_doc_scanner 通道,在 OHOS 侧增加了标准 FlutterPlugin 注册、版本查询和四个扫描方法的明确错误回传。Demo 通过 Flutter OH 3.47.4 构建成签名 HAP,并在 HarmonyOS API 26 真机上完成安装、启动和点击验证。
当前成果是“可构建、可安装、接口行为清楚”的鸿蒙适配基线,不代表鸿蒙扫描器已经完成。真正接入扫描能力时,应继续补充系统 API、权限、文件导出、取消流程和图片/PDF 结果回归。适配代码和说明都在 AtomGit 适配分支,复现时建议锁定 0.0.21-ohos.1 TAG。
十三、参考资料和仓库地址
- 适配仓库主页
- 适配分支
- 适配 TAG
- 最终提交 b5efbd917d078912e917be6e8adb0bf328048025
- OHOS 插件导出入口
- ArkTS MethodChannel 实现
- Dart 公共入口
- Dart MethodChannel
- 鸿蒙适配说明
- pub.dev 包页面
- pub.dev 0.0.21 版本页
- pub.dev API 文档
- 上游 GitHub 仓库
- 上游 LICENSE
- Flutter 官方插件与平台通道文档
- Flutter OH 鸿蒙 SDK 仓库
- HarmonyOS 开发者官方文档
- HarmonyOS API 参考
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐

所有评论(0)