爱吃大芒果头像

Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 cunning_document_scanner

文档扫描在移动端看起来像一个“打开相机、拍几页、返回文件”的小功能,实际实现却分成了两条完全不同的技术路线:Android 侧依赖 ML Kit Document Scanner,iOS 侧依赖 VisionKit。cunning_document_scanner 把这些平台差异包装成一套 Dart API。把它带到鸿蒙时,最容易踩的坑不是写不出 ArkTS 插件注册,而是把“接口能调用”误认为“扫描能力已经完成”。

本文以 cunning_document_scanner 3.0.3 为例,记录我如何保留原有 Dart API、补齐 Flutter OH 插件入口、让鸿蒙端明确返回能力边界,并在 HarmonyOS API 26 模拟器上完成构建、安装、启动和按钮调用验证。当前分支可以运行 Demo,但鸿蒙系统扫描器 UI 尚未接入,所以模拟器最终显示 UNSUPPORTED。这个结果是适配过程的一部分,也是比返回空列表更可靠的结论。

适配仓库: oh-flutter/cunning_document_scanner

适配分支: feat/ohos_cunning_document_scanner_3.0.3

适配 TAG: 3.0.3-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
getPictures返回 UNSUPPORTED图 2
cleanCache返回成功,无异常图 3
鸿蒙扫描器 UI尚未接入适配边界

为了确认缓存清理入口也可以单独复现,我又在模拟器上点击了“清理扫描缓存”按钮:

文章配图

图 3:调用 cleanCache 后返回成功,没有伪造扫描文件残留。

二、这个库原来是怎么工作的

上游包的公共入口是 CunningDocumentScanner。它的 getPictures 支持页数上限、来源(相机或相册)、Android 的扫描模式、iOS 的扫描选项,以及是否直接返回 PDF。Dart 层通过名为 cunning_document_scanner 的 MethodChannel 把参数传给原生端。

Dart CunningDocumentScanner
  -> MethodChannel('cunning_document_scanner')
  -> Android ML Kit / iOS VisionKit
  -> 图片路径列表或 PDF 路径

读源码时有一个细节值得单独记下来:getPictures 对“取消”的语义做了归一化,Android 历史上把取消报告为空列表,iOS 报告为 null,插件在 Dart 层把两者都转换成 null。这意味着业务代码里 null 的含义是“用户取消了调用”,而不是“平台不支持”。本次适配把这个区别原样保留:取消走 null,平台能力缺失走标准错误。

// cunning_document_scanner 3.0.3 的取消语义(上游源码)
final List<dynamic>? pictures;
try {
  pictures = await _channel.invokeMethod<List<dynamic>>('getPictures', {
    'noOfPages': noOfPages,
    'isGalleryImportAllowed': isGalleryImportAllowed,
    'scannerSource': resolvedSource.methodChannelValue,
    'androidScannerMode': androidScannerMode.methodChannelValue,
    'asPdf': asPdf,
    if (iosScannerOptions != null)
      'iosScannerOptions': iosScannerOptions.toMap(),
  });
} on PlatformException catch (e) {
  throw CunningDocumentScannerException(
    e.message ?? 'The document scan failed',
    code: e.code,
  );
}

// Android 空列表和 iOS null 都归一为 null:取消不等于失败
if (pictures == null || pictures.isEmpty) {
  return null;
}

OHOS 端没有把 Android 的 ML Kit 或 iOS 的 VisionKit 直接搬过来,也没有伪造一个空的扫描结果,而是把当前能力状态通过标准错误返回给 Dart 层。

三、实际环境

Flutter 鸿蒙环境搭建不在本文重复展开,直接参考 Flutter OH 环境搭建指南。

组件版本
Flutter OH3.47.4-ohos-1.0.4
Dart3.13.3
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26
测试设备DevEco 模拟器 Pura X View(HarmonyOS 7.0.0.105,API 26)

本次验证使用 DevEco Studio 26.0.0 自带的 Pura X View 模拟器,系统版本与目标真机同为 HarmonyOS 7.0.0.105 / API 26;HAP 的签名、安装与启动链路和真机一致。
| 目标库 | cunning_document_scanner 3.0.3 |

flutter --version
hdc list targets

四、确认上游版本并同步到 AtomGit

上游包地址是 GitHub vicajilau/cunning_document_scanner,版本 3.0.3 使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现,因此从上游基线开始增加平台目录。

仓库初始适配提交为 1f21bd6,完成后分支最新提交为 c212fdc。

git clone https://atomgit.com/oh-flutter/cunning_document_scanner.git
cd cunning_document_scanner
git log --oneline -5
git switch feat/ohos_cunning_document_scanner_3.0.3
git remote -v

先确认仓库、版本和远程地址,再开始改代码,后面出现编译问题时才容易区分是上游问题、工具链问题还是本次改动引入的问题。

五、创建适配分支并检查工程结构

git switch -c feat/ohos_cunning_document_scanner_3.0.3
git branch --show-current

关键目录如下:

cunning_document_scanner/
├── lib/                                          # 原有 Dart API 和 MethodChannel
├── ohos/
│   ├── index.ets                                 # OHOS 插件导出入口
│   └── src/main/ets/components/plugin/
│       └── CunningDocumentScannerPlugin.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: CunningDocumentScannerPlugin

pluginClass 必须和 ohos/index.ets 导出的类名一致,否则 Dart 端能编译,运行时却不会注册插件。

六、适配过程:先对齐契约,再写 ArkTS

我先从 lib/cunning_document_scanner.dart 和 lib/cunning_document_scanner_method_channel.dart 读取现有接口,没有改动公开方法名和参数:

Dart 方法参数OHOS 本次策略
getPlatformVersion无返回 OpenHarmony 3.47.4
getPictures页数、来源、扫描模式、iOS 选项、asPdf返回 UNSUPPORTED
cleanCache无安全无操作,返回成功

这里有一个容易忽略的区别:null 是“调用完成但没有结果”的正常数据形态,在这条库的语义里专指用户取消扫描;UNSUPPORTED 是“当前平台没有这项能力”的错误形态。两者混用会让业务层把“鸿蒙尚未适配”误认为“用户取消”。

OHOS 插件骨架使用 Flutter OH 的 FlutterPlugin、MethodCallHandler 和 MethodChannel:

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel?.setMethodCallHandler(null);
  this.channel = new MethodChannel(
    binding.getBinaryMessenger(),
    'cunning_document_scanner',
  );
  this.channel.setMethodCallHandler(this);
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  this.channel?.setMethodCallHandler(null);
  this.channel = null;
}

注册时使用的通道字符串必须和 Dart 层完全相同。解绑时清理 handler,避免引擎重复创建后出现重复回调。

核心方法分支如下:

onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case 'getPlatformVersion':
      result.success('OpenHarmony 3.47.4');
      return;
    case 'cleanCache':
      result.success(null);
      return;
    case 'getPictures':
      result.error(
        'UNSUPPORTED',
        'Document scanning is not available on OpenHarmony yet.',
        null,
      );
      return;
    default:
      result.notImplemented();
  }
}

result.error 会被 Dart MethodChannel 捕获为 PlatformException,插件 Dart 实现再把它转换成 CunningDocumentScannerException 并保留 code。Demo 只捕获这个异常类型并展示 code 和 message,所以运行页面能看到 UNSUPPORTED,而不是一串难以判断的空值。

cleanCache 是这条库里少有的可以直接映射的能力:OHOS 端当前没有插件私有缓存文件需要清理,返回成功可以保证调用方生命周期代码不必为平台差异写分支。

七、构建配置与签名

示例工程的目标 SDK 分为 compatibleSdkVersion 和 targetSdkVersion,两者都要对齐到 26,才能和 @ohos/flutter_ohos 的最低要求一致:

{
  "name": "default",
  "compatibleSdkVersion": "26.0.0",
  "targetSdkVersion": "26.0.0",
  "runtimeOS": "HarmonyOS"
}

为了让本机 DevEco 调试签名可以直接为设备安装服务,我把 Demo 的 AppScope/app.json5 统一成 com.example.flutter_oh_demo,并在 example/ohos/build-profile.json5 中配置调试签名。证书、p12、p7b 和密码没有提交到仓库,读者应在自己的 DevEco Studio 中生成调试签名。

八、通过 AtomGit 引入适配版

在另一个 Flutter OH 应用中使用时,建议锁定 TAG:

dependencies:
  cunning_document_scanner:
    git:
      url: https://atomgit.com/oh-flutter/cunning_document_scanner.git
      ref: 3.0.3-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;点击“清理扫描缓存”,得到图 3。

flutter analyze 在示例工程和插件本体都通过。设备验证覆盖的是插件注册、通道连通、版本查询和两条方法的返回语义,不代表扫描 UI 已经可用。

十、适配结果和当前边界

层次已完成内容尚未完成内容
Dart API保留原方法、模型和异常类型无
MethodChannel通道名和方法名全部对齐无
OHOS 插件注册、版本查询、缓存清理、错误回传真正的扫描 UI
DemoAPI 26 模拟器启动和按钮调用拍照、裁剪、生成图片/PDF
文档README.OpenHarmony_CN.md、CHANGELOG无

Android 的 ML Kit 和 iOS 的 VisionKit 不能通过改一个平台声明就变成鸿蒙能力。下一步如果要完成真正扫描,需要选定 HarmonyOS 的相机、图像处理和文件导出 API,补充相机权限,处理前后台切换和用户取消,并为图片、PDF 两种返回结果分别做设备回归。当前提交明确告诉使用者“这个能力还没有接入”,比让业务误以为扫描成功更容易维护。

十一、FAQ

Q1:为什么 HAP 构建成功,页面仍然显示不支持?

  • 现象: HAP 可以签名、安装并启动,但点击扫描后显示 UNSUPPORTED。
  • 原因: 当前 OHOS ArkTS 端只完成插件注册、版本查询和错误语义,没有接入系统扫描器 UI。
  • 解决方法: 保留明确错误,后续接入真正的 HarmonyOS 扫描实现后再替换对应分支。
  • 验证结果: 图 2 证明错误从 ArkTS 经 MethodChannel 到达 Dart 页面。

Q2:为什么不能直接返回空列表?

  • 现象: 如果 OHOS 端返回空列表,Dart 业务会把 getPictures 结果当成用户取消。
  • 原因: 这条库把空列表归一化为 null,null 专指“用户取消”。
  • 解决方法: getPictures 统一调用 result.error('UNSUPPORTED', ...),把能力缺失交给异常通道。
  • 验证结果: Demo 显示错误码和英文原因,状态可追踪。

Q3:cleanCache 为什么可以直接返回成功?

  • 现象: 鸿蒙端没有真实扫描文件,但调用 cleanCache 不报错。
  • 原因: 插件在 OHOS 端没有写入私有缓存,清理动作是安全无操作。
  • 解决方法: 返回 result.success(null),保证调用方的释放代码在所有平台行为一致。
  • 验证结果: 图 3 显示调用完成后页面状态正常,没有异常。

十二、总结

这次适配保留了 cunning_document_scanner 3.0.3 原有 Dart 接口和 cunning_document_scanner 通道,在 OHOS 侧增加了标准 FlutterPlugin 注册、版本查询、缓存清理和 getPictures 的明确错误回传。Demo 通过 Flutter OH 3.47.4 构建成签名 HAP,并在 HarmonyOS API 26 模拟器上完成安装、启动和按钮验证。

当前成果是“可构建、可安装、接口行为清楚”的鸿蒙适配基线,不代表鸿蒙扫描器已经完成。真正接入扫描能力时,应继续补充系统 API、权限、文件导出、取消流程和图片/PDF 结果回归。适配代码和说明都在 AtomGit 适配分支,复现时建议锁定 3.0.3-ohos.1 TAG。

十三、参考资料和仓库地址

欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2502_93915572/article/details/168128075

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--