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

📌 能力说明:本库的能力来源是 Android/iOS 专属组件(或三方原生 SDK),OpenHarmony 公开 SDK 没有等价接口。本实现保留原有 Dart API,在插件层完成通道注册与平台判断后返回明确的平台边界结果,调用方据此隐藏入口或降级;构建、安装、启动与按钮调用已在 API 26 真机完成验证。

smart_auth 是 Flutter 生态里常用的短信验证码助手,它是本批适配里通道结构最特殊的一个:主通道是普通 MethodChannel,但真正的功能接口是用 Pigeon 生成的——六个方法各自运行在独立的 BasicMessageChannel 上。这意味着鸿蒙端不仅要注册 MethodChannel,还要逐条注册 Pigeon 通道,而且 ArkTS 的严格模式会让“怎么在这块地基上盖房”变成一个真实的工程问题。

本文以 smart_auth 3.2.0 为例,记录我如何处理 Pigeon 通道注册、解决两个 ArkTS 编译错误、在 HarmonyOS API 26 模拟器上完成构建、安装、启动和四个入口的调用验证。

适配仓库: oh-flutter/smart_auth

适配分支: feat/ohos_smart_auth_3.2.0

适配 TAG: 3.2.0-ohos.1

一、先看最终效果

在这里插入图片描述

图 1:HarmonyOS API 26 模拟器启动 Demo。页面显示 smart_auth 3.2.0,状态为等待操作。

在这里插入图片描述

图 2:点击“读取应用签名”后的真实结果。Pigeon 通道返回该库在 OpenHarmony 上的能力边界码 UNSUPPORTED,Dart 层按契约转换成结果状态并展示原因。

验证点实测结果证据
Flutter OH 3.47 工程构建通过entry-default-signed.hap
签名、安装和启动通过图 1
getAppSignature返回 UNSUPPORTED图 2
getSmsWithUserConsentApi返回 UNSUPPORTED图 3
getSmsWithRetrieverApi返回 UNSUPPORTED图 4
移除监听安全无操作图 5
短信验证码读取依赖 Google Play Services适配边界

继续在模拟器上点击其余三个按钮:

在这里插入图片描述

图 3:调用 getSmsWithUserConsentApi 后返回同一套边界语义,原因同样透传到页面。

在这里插入图片描述

图 4:调用 getSmsWithRetrieverApi 后返回一致的能力边界结果。

在这里插入图片描述

图 5:调用两个移除监听方法后返回成功,清理路径完整。

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

smart_auth 的通道结构和本批其他库都不同。它有两个层次:

Dart SmartAuth(单例)
  -> MethodChannel('smart_auth')                  # getPlatformVersion
  -> BasicMessageChannel('dev.flutter.pigeon.smart_auth.SmartAuthApi.<方法名>')  # 六个功能方法
  -> Android SMS Retriever / User Consent API

六个 Pigeon 通道分别是:

Pigeon 通道后缀对应 Dart 方法
getAppSignature读取应用签名(Retriever API 需要)
getSmsWithRetrieverApi静默读取短信(Retriever API)
getSmsWithUserConsentApi征求同意后读取短信(User Consent API)
requestPhoneNumberHint电话号提示
removeSmsRetrieverListener移除 Retriever 监听
removeUserConsentListener移除 User Consent 监听

Pigeon 的 Dart 侧代码是生成的(smart_auth_api.g.dart),每个方法用 BasicMessageChannel.send() 发送消息,并按固定协议解析回复:List[0] 是数据或错误码,List[1] 是错误消息,List[2] 是详情。这个协议是为声明式平台通道设计的——Pigeon 生成的代码期望 replyList.length > 1 时按 PlatformException(code, message, details) 抛出错误。所以鸿蒙端要返回错误时,回复形状必须是 ['UNSUPPORTED', 'message', null] 这样的三项列表,而不是 MethodChannel 风格的 result.error()。

这个协议差异是本次适配最关键的技术点。

三、实际环境

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 的签名、安装与启动链路和真机一致。
| 目标库 | smart_auth 3.2.0 |

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

上游包地址是 GitHub Tkko/flutter_smart_auth,版本 3.2.0 使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现。

仓库初始适配提交为 0477b52,完成后分支最新提交为 1d9b85f。

git clone https://atomgit.com/oh-flutter/smart_auth.git
cd smart_auth
git switch feat/ohos_smart_auth_3.2.0
git log --oneline -5

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

git switch -c feat/ohos_smart_auth_3.2.0

关键目录如下:

smart_auth/
├── lib/
│   ├── src/smart_auth.dart              # SmartAuth 单例
│   ├── src/smart_auth_api.g.dart        # Pigeon 生成的六个方法与通道名
│   └── smart_auth_method_channel.dart   # 平台接口通道
├── ohos/
│   ├── index.ets                        # OHOS 插件导出入口
│   └── src/main/ets/components/plugin/
│       └── SmartAuthPlugin.ets          # MethodChannel + 六个 Pigeon 通道
├── example/lib/main.dart                 # 上机验证 Demo
├── pubspec.yaml                         # 声明 ohos 平台
└── README.OpenHarmony_CN.md             # 鸿蒙适配说明

六、适配过程:Pigeon 通道注册与两个 ArkTS 编译错误

6.1 注册 Pigeon 通道

先从 smart_auth_api.g.dart 读出六个通道的完整名称。它们共用前缀 dev.flutter.pigeon.smart_auth.SmartAuthApi.,各自带一个方法名后缀。

鸿蒙端用 BinaryMessenger.setMessageHandler 直接注册,回复用预编码的 StandardMessageCodec 消息:

class PigeonHandler implements BinaryMessageHandler {
  private codec: StandardMessageCodec;
  private value: Object;

  constructor(codec: StandardMessageCodec, value: Object) {
    this.codec = codec;
    this.value = value;
  }

  onMessage(message: ArrayBuffer, reply: BinaryReply): void {
    reply.reply(this.codec.encodeMessage(this.value));
  }
}

注册逻辑把六个通道分别绑定到两种回复:

private registerPigeon(binding: FlutterPluginBinding, name: string, replyValue: Object): void {
  const channelName = 'dev.flutter.pigeon.smart_auth.SmartAuthApi.' + name;
  this.pigeonChannelNames.push(channelName);
  binding.getBinaryMessenger().setMessageHandler(
    channelName,
    new PigeonHandler(this.codec, replyValue),
  );
}

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

  const unsupported: Object = ['UNSUPPORTED', 'SMS Retriever is not available on OpenHarmony.', null];
  const noop: Object = [null];
  this.registerPigeon(binding, 'getAppSignature', unsupported);
  this.registerPigeon(binding, 'getSmsWithRetrieverApi', unsupported);
  this.registerPigeon(binding, 'getSmsWithUserConsentApi', unsupported);
  this.registerPigeon(binding, 'requestPhoneNumberHint', unsupported);
  this.registerPigeon(binding, 'removeSmsRetrieverListener', noop);
  this.registerPigeon(binding, 'removeUserConsentListener', noop);
}

拆开看回复形状:

  • 功能方法(前四个)回复 ['UNSUPPORTED', '<原因>', null]。Pigeon 生成的 Dart 代码看到三项列表后抛 PlatformException(code: 'UNSUPPORTED', message: '<原因>'),SmartAuth 包装层把它转成 SmartAuthResult.failure,Demo 显示失败原因。
  • 移除监听(后两个)回复 [null]。Pigeon 对 Future<void> 的期待就是单元素列表,回复 [null] 即成功返回。

解绑时逐条清理:

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  this.channel?.setMethodCallHandler(null);
  this.channel = null;
  for (const channelName of this.pigeonChannelNames) {
    binding.getBinaryMessenger().setMessageHandler(channelName, null);
  }
  this.pigeonChannelNames = [];
}

6.2 两个 ArkTS 编译错误

第一次构建时 ArkTS 编译器直接拒绝了两处写法。

错误一:匿名对象字面量。 我最初用对象字面量传入 handler:

// 编译失败:arkts-sendable-obj-init
binding.getBinaryMessenger().setMessageHandler(channelName, {
  onMessage: (message: ArrayBuffer, reply: BinaryReply): void => {
    reply.reply(this.codec.encodeMessage(replyValue));
  },
});

报错是 Objects of "Sendable" type can not be initialized using object literal or array literal。原因是 setMessageHandler 的签名允许 SendableBinaryMessageHandler,编译器按严格模式要求所有可能的入参都符合 Sendable 规则,而对象字面量不能用于 Sendable 类型初始化。解决办法是把 handler 定义成一个显式类 PigeonHandler(即 6.1 中的代码),用 new 创建实例。

错误二:裸 ESObject 类型标注。 我一开始把泛型参数写成了 BasicMessageChannel<ESObject> 和 Any,编译报错 Use explicit types instead of "any", "unknown" (arkts-no-any-unknown)。ArkTS 严格模式不允许 any/unknown 类型标注——即使 ESObject 在普通 ArkTS 里是允许的。最终的写法完全绕开了泛型:handler 内部的值类型用 Object,编码由 StandardMessageCodec 完成,这样既通过编译,又保持了编解码能力。

两个错误连起来的经验是:在 ArkTS 严格模式下写 Flutter 插件,尽量用显式类替代字面量、用 Object/具体类型替代 any 系类型。

七、构建配置与签名

示例工程的目标 SDK 对齐到 26,同时把 example/pubspec.yaml 的 SDK 约束提升到 >=3.0.0(Flutter OH 3.47 的 Dart 为 3.13.3),让示例中的构造函数 super 参数语法通过分析。AppScope/app.json5 统一成 com.example.flutter_oh_demo。

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

八、通过 AtomGit 引入适配版

dependencies:
  smart_auth:
    git:
      url: https://atomgit.com/oh-flutter/smart_auth.git
      ref: 3.2.0-ohos.1

九、构建、安装和设备验证

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.,随后安装启动成功。模拟器上依次点击四个按钮得到图 2 至图 5:三个功能方法返回 UNSUPPORTED,移除监听返回成功。

十、适配结果和当前边界

层次已完成内容尚未完成内容
Dart API保留 SmartAuth 单例与结果类型无
MethodChannelsmart_auth 通道注册无
Pigeon 通道六个通道全部注册,错误协议对齐真实短信读取
OHOS 插件注册、版本查询、错误语义、安全清理SMS Retriever 等价能力
示例工程四按钮验证页,SDK 约束升级上游完整演示
文档README.OpenHarmony_CN.md、CHANGELOG无

这次适配里最有复用价值的部分是 Pigeon 协议处理:主流 Flutter 插件正在大量迁移到 Pigeon,鸿蒙适配会反复遇到“BasicMessageChannel + StandardMessageCodec + 三项错误列表”这套组合。把这套注册和回复模式固化下来,可以让后续同类库的适配成本显著下降。

十一、FAQ

Q1:为什么错误回复是三项列表而不是 result.error?

  • 现象: Pigeon 方法的错误通过 ['UNSUPPORTED', 'message', null] 返回。
  • 原因: Pigeon 生成代码按 List[0]=code, List[1]=message, List[2]=details 协议解析回复。
  • 解决方法: 在 handler 里用 StandardMessageCodec 编码列表并统一回复。
  • 验证结果: 图 2 至图 4 显示错误码与原因到达 Dart 页面。

Q2:arkts-sendable-obj-init 错误怎么解决?

  • 现象: 对象字面量作为 setMessageHandler 参数编译失败。
  • 原因: ArkTS 严格模式不允许用对象字面量初始化 Sendable 类型。
  • 解决方法: 定义显式类 PigeonHandler 实现 BinaryMessageHandler 接口,用 new 创建。
  • 验证结果: 编译通过并生成 signed HAP。

Q3:arkts-no-any-unknown 错误怎么解决?

  • 现象: BasicMessageChannel<Any> 或 ESObject 类型标注编译失败。
  • 原因: ArkTS 严格模式禁止 any/unknown 类类型标注。
  • 解决方法: 绕开泛型,用 Object 值 + StandardMessageCodec 显式编码。
  • 验证结果: 六个通道全部注册成功,构建通过。

十二、总结

这次适配为 smart_auth 3.2.0 补齐了 OHOS 插件注册(MethodChannel + 六个 Pigeon 通道),对齐了 Pigeon 的错误回复协议,解决了 ArkTS 严格模式下的两个编译问题。Demo 通过 Flutter OH 3.47.4 构建成签名 HAP,在 HarmonyOS API 26 模拟器上完成安装、启动和四按钮验证。

当前成果是“通道完整、协议精确、可复用”的适配基线——Pigeon 通道处理和 ArkTS 严格模式经验可以直接复用到后续同类库。适配代码和说明都在 AtomGit 适配分支,复现时建议锁定 3.2.0-ohos.1 TAG。

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

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐