短信验证码自动填充是移动端登录流程里的高频优化点。otp_autofill 的实现完全建立在 Android 的两套系统机制上:SMS Retriever API(后台静默读取格式化短信)和 User Consent API(弹窗征求用户同意后读取)。它还会组合 otp_surfstudio 通道提供电话号提示和签名获取。把这条库带到鸿蒙时,适配的边界从第一步就确定了:SMS Retriever 是 Google Play Services 的组件,鸿蒙没有等价机制。

本文以 otp_autofill 4.1.0 为例,记录我如何在鸿蒙端注册双通道、把 Android 专属方法映射为明确的 UNSUPPORTED 语义,并在 HarmonyOS API 26 模拟器上完成构建、安装、启动和三个入口的调用验证。

适配仓库: oh-flutter/otp_autofill

适配分支: feat/ohos_otp_autofill_4.1.0

适配 TAG: 4.1.0-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
getAppSignature返回 UNSUPPORTED图 2
startListenRetriever返回 UNSUPPORTED图 3
stopListenForCode安全无操作图 4
短信自动填充依赖 Google Play Services适配边界

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

图 3:调用 startListenRetriever 后返回同一套 UNSUPPORTED 语义。

图 4:调用 stopListenForCode 安全完成,清理路径完整。

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

otp_autofill 的 Dart 层分两半:OTPInteractor 直接使用名为 otp_surfstudio 的 MethodChannel;插件注册部分使用 otp_autofill 通道。

Dart OTPInteractor
  -> MethodChannel('otp_surfstudio')     # getAppSignature / startListenRetriever 等
  -> Android SMS Retriever / User Consent
Dart 插件平台接口
  -> MethodChannel('otp_autofill')       # getPlatformVersion

读源码时有个重要细节:OTPInteractor 的每个 Android 专属方法都先做平台判断,非 Android 平台直接抛 UnsupportedPlatform:

Future<String?> getAppSignature() async {
  if (_platform.isAndroid) {
    return _channel.invokeMethod<String>(getAppSignatureMethod);
  } else {
    throw UnsupportedPlatform();
  }
}

这个设计对我们很有用:它意味着鸿蒙端不需要在 ArkTS 层拦截这些方法——Dart 层自己就会先拦住。那为什么还要在 ArkTS 里注册 otp_surfstudio 通道并实现同样的错误分支?两个原因:一是 iOS 平台上 startListenUserConsent 这类流式方法不走平台判断,会直接打到通道;二是随着上游代码演进,平台判断可能被重构,通道层有兜底更稳。

stopListenForCode 是唯一一个不带平台判断的方法,它在所有平台都会走通道,所以鸿蒙端把它做成安全无操作。

三、实际环境

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

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

上游包地址是 GitHub surfstudio/flutter-otp-autofill,版本 4.1.0 使用 Apache License 2.0。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现。

仓库初始适配提交为 7ae862f,完成后分支最新提交为 1165d0a。

git clone https://atomgit.com/oh-flutter/otp_autofill.git
cd otp_autofill
git switch feat/ohos_otp_autofill_4.1.0
git log --oneline -5

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

git switch -c feat/ohos_otp_autofill_4.1.0

关键目录如下:

otp_autofill/
├── lib/
│   ├── src/otp_interactor.dart          # OTPSurfstudio 通道与平台判断
│   ├── src/otp_text_edit_controller.dart  # OTP 输入控制器
│   └── otp_autofill_platform_interface.dart
├── ohos/
│   ├── index.ets                        # OHOS 插件导出入口
│   └── src/main/ets/components/plugin/
│       └── OtpAutofillPlugin.ets        # 双通道 MethodChannel 实现
├── example/lib/main.dart                 # 上机验证 Demo
├── pubspec.yaml                         # 声明 ohos 平台
└── README.OpenHarmony_CN.md             # 鸿蒙适配说明

六、适配过程:双通道注册与三组语义

OHOS 插件同时注册 otp_autofill 和 otp_surfstudio 两条通道:

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

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

方法映射分三组:

Dart 方法通道OHOS 本次策略
getPlatformVersionotp_autofill返回 OpenHarmony 3.47.4
getTelephoneHint / startListenUserConsent / startListenRetriever / getAppSignatureotp_surfstudio返回 UNSUPPORTED 错误
stopListenForCodeotp_surfstudio安全无操作
onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case 'getPlatformVersion':
      result.success('OpenHarmony 3.47.4');
      return;
    case 'getTelephoneHint':
    case 'startListenUserConsent':
    case 'startListenRetriever':
    case 'getAppSignature':
      result.error(
        'UNSUPPORTED',
        'SMS Retriever and User Consent APIs are not available on OpenHarmony.',
        null,
      );
      return;
    case 'stopListenForCode':
      result.success(null);
      return;
    default:
      result.notImplemented();
  }
}

这四个方法共用一个错误分支是刻意的:它们的能力来源是同一套 Android 机制,能力不存在时用同一条消息说明原因,业务侧只需要匹配一次 UNSUPPORTED 就能覆盖全部入口。

示例工程方面,上游示例使用 SampleStrategy 和 OTPTextEditController 做完整演示;我在改造时保留 OTPTextEditController 的核心用法(它内部会调用 startListenUserConsent),把页面收敛成三个按钮:签名、Retriever 监听、停止监听。示例的 dev_dependencies 里还补上了缺失的 flutter_test 声明,让 flutter analyze 干净通过。

七、构建配置与签名

示例工程的目标 SDK 对齐到 26,AppScope/app.json5 统一成 com.example.flutter_oh_demo,与 DevEco 调试签名对应。

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

八、通过 AtomGit 引入适配版

dependencies:
  otp_autofill:
    git:
      url: https://atomgit.com/oh-flutter/otp_autofill.git
      ref: 4.1.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 至图 4:签名与 Retriever 监听返回 UNSUPPORTED,停止监听安全完成。

十、适配结果和当前边界

层次已完成内容尚未完成内容
Dart API保留 OTPInteractor 与控制器无
MethodChannelotp_autofill 与 otp_surfstudio 双注册无
OHOS 插件注册、版本查询、四方法错误语义、安全停止真实验证码读取
示例工程三按钮验证页,补齐 flutter_test 依赖上游完整演示
文档README.OpenHarmony_CN.md、CHANGELOG无

鸿蒙侧如果要实现验证码填充,替代路径可能是系统级的验证码建议能力(例如输入法或系统组件提供的短信验证码填充),而不是复刻 SMS Retriever。当前实现保证契约稳定:应用在鸿蒙上启动正常,所有 Android 专属调用给出可识别的 UNSUPPORTED,清理调用不中断。

十一、FAQ

Q1:为什么 Dart 已经做了平台判断,还要注册两个通道?

  • 现象: OTPInteractor 在非 Android 平台抛 UnsupportedPlatform,看起来鸿蒙端不需要任何实现。
  • 原因: 上游只有部分方法带平台判断,且平台判断可能随版本变化;通道层兜底更稳。
  • 解决方法: 注册 otp_surfstudio 与 otp_autofill,并在通道层实现同语义的 UNSUPPORTED。
  • 验证结果: 图 2 与图 3 显示错误从 ArkTS 经通道到达 Dart 页面。

Q2:stopListenForCode 为什么可以直接返回成功?

  • 现象: 没有启动监听,停止监听也不报错。
  • 原因: 它是唯一无平台判断的方法,出现在 dispose 等清理路径里。
  • 解决方法: 安全无操作保证清理链不中断。
  • 验证结果: 图 4 显示调用完成。

Q3:为什么示例要去掉 SampleStrategy?

  • 现象: 示例从带策略类的完整演示收敛成三个按钮。
  • 原因: 策略类只在真实收到短信时有意义,鸿蒙端收不到短信,保留它会造成误导。
  • 解决方法: 用最小验证面覆盖通道与错误语义。
  • 验证结果: analyze 与 signed HAP 构建均通过。

十二、总结

这次适配为 otp_autofill 4.1.0 补齐了 OHOS 插件注册(双通道),把 Android 专属入口映射为统一的 UNSUPPORTED 语义,保留 stopListenForCode 的安全无操作。Demo 通过 Flutter OH 3.47.4 构建成签名 HAP,在 HarmonyOS API 26 模拟器上完成安装、启动和调用验证。

当前成果是“契约完整、语义诚实”的适配基线。适配代码和说明都在 AtomGit 适配分支,复现时建议锁定 4.1.0-ohos.1 TAG。

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

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

Logo

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

更多推荐