Flutter 三方库 app_tracking_transparency 的鸿蒙适配教程
Flutter 三方库 app_tracking_transparency 的鸿蒙适配教程
本文配套仓库:https://atomgit.com/oh-flutter/app_tracking_transparency(TAG:
2.0.7-ohos-1.0.0-beta.1,分支:feat/ohos_app_tracking_transparency_2.0.7)。本文解决的是另一件事:从上游 GitHub 仓库开始,把 app_tracking_transparency 完整适配到 OpenHarmony / HarmonyOS 平台,并在模拟器上验证。
app_tracking_transparency 是 GitHub 上 deniza 开发的一个 Flutter 插件(MIT 协议,2.0.7),封装了 iOS 14+ 的 ATT(App Tracking Transparency)能力:查询跟踪授权状态、弹出系统跟踪授权弹窗、读取广告标识符(IDFA)。它原本只有 iOS 平台实现——Android 上没有 ATT 概念,接口由 Dart 侧直接返回 notSupported 或空字符串;唯独没有鸿蒙。而鸿蒙 Flutter 应用如果有一套跨平台的合规采集逻辑,需要一个语义对齐的鸿蒙实现,让同一份调用代码在三个平台上都能跑通。本文完整走一遍社区三方库适配的标准流程:把上游仓库同步到 AtomGit,拉到宿主机,建适配分支,用命令自动补全 ohos 目录,补全 Dart 与 ArkTS 两侧实现,补齐适配说明文件后提交分支与 TAG,最后用仓库自带的 example 在鸿蒙模拟器上验证。
一、环境搭建
鸿蒙 Flutter 开发环境(ohos 版 SDK、DevEco Studio、签名配置)的完整搭建步骤,官方指南已经写得很细,直接照做即可:
适配工作比单纯使用多一项要求:终端里 flutter 命令必须指向 ohos 版 SDK,因为后文自动补全 ohos 目录靠的是它提供的 flutter create --platforms ohos 能力。环境装好后用 flutter devices 确认能识别鸿蒙设备(真机或模拟器均可)。本文实测使用的环境:
| 项 | 版本 |
|---|---|
| Flutter(ohos 版) | 3.41.10-ohos-1.0.1 |
| 编译 SDK | 5.1.0(18) |
| 实测设备 | HarmonyOS Emulator 模拟器(HarmonyOS 7.0.0.105 / API 26) |
二、适配过程
2.1 将上游仓库同步到 AtomGit
鸿蒙 Flutter 三方库社区(oh-flutter 组织)托管在 AtomGit,上游项目在 GitHub,适配的第一步是把上游代码完整迁入 AtomGit 上为它新建的目标仓库 oh-flutter/app_tracking_transparency。做法是把上游克隆下来,添加 AtomGit 远端后整库推送,保留全部 commit 历史与 TAG,后续上游发新版时也能用同样的方式增量同步:
# 克隆上游仓库,目录名与目标仓库保持一致
git clone https://github.com/deniza/app_tracking_transparency.git app_tracking_transparency
cd app_tracking_transparency
# 关联 AtomGit 目标仓库
git remote add atomgit https://atomgit.com/oh-flutter/app_tracking_transparency.git
# 推送全部分支与 TAG
git push atomgit --all
git push atomgit --tags
推送完成后,打开 AtomGit 上目标仓库的页面,能看到与上游一致的提交历史和源码目录:

图一:同步完成后 AtomGit 目标仓库的代码页
2.2 拉取代码到宿主机
从目标仓库把代码拉到本地,后续所有操作都在这份代码上进行:
git clone https://atomgit.com/oh-flutter/app_tracking_transparency.git
cd app_tracking_transparency
此时的目录是上游的原始结构。app_tracking_transparency 是标准的单包插件,所有平台实现与 Dart 接口都在仓库根目录:
app_tracking_transparency/
├── example/ # 上游自带示例工程
├── images/ # README 配图
├── ios/ # iOS 平台实现(ATT 授权弹窗 + IDFA)
├── lib/
│ └── app_tracking_transparency.dart # Dart 接口:TrackingStatus 枚举与三个静态接口
├── test/
├── CHANGELOG.md
├── LICENSE # MIT
├── README.md
└── pubspec.yaml
注意看:这个库比常见插件更"瘦"——目录里只有 ios/,连 android/ 都没有,pubspec 的插件注册节点里也只有 iOS 一个平台。Android 上之所以接口还能调用,是 Dart 侧用 defaultTargetPlatform 判断后直接返回 notSupported 或空字符串兜底的。所以本次适配要做的不只是"把 iOS 的原生实现翻译成 ArkTS",而是按社区适配约定,为鸿蒙定义一套与 Android 对齐的返回语义。这一点在 2.4 节展开。

图二:clone 完成后的仓库原始目录
2.3 创建适配分支并补全 ohos 目录结构
先建适配分支。社区约定分支名为 feat/ohos_<库名>_<版本号>,版本号取自上游 pubspec 里的 version: 2.0.7:
git checkout -b feat/ohos_app_tracking_transparency_2.0.7
接下来一条命令补全 ohos 目录。flutter create --platforms ohos . 是 ohos 版 Flutter SDK 提供的能力:读取当前目录 pubspec 的包名与插件声明,自动生成鸿蒙平台目录并追加注册节点:
flutter create --platforms ohos .
命令执行后,仓库里多出一个 ohos/ 目录,pubspec.yaml 也被自动改了两处。生成的目录结构(剔除构建产物)如下:
ohos/
├── src/main/ets/components/plugin/
│ └── AppTrackingTransparencyPlugin.ets # 插件模板:ArkTS 实现入口,此刻是空的
├── src/main/module.json5 # 模块配置
├── BuildProfile.ets # 构建时生成的版本信息
├── index.ets # 插件导出入口
├── oh-package.json5 # 包描述:name / version / main / 对引擎 HAR 的依赖
├── build-profile.json5 # 构建配置
└── hvigorfile.ts # hvigor 构建脚本
各文件的职责:index.ets 把插件类导出给引擎侧;oh-package.json5 声明这是一个依赖 @ohos/flutter_ohos(引擎 HAR 包)的 ArkTS 包;module.json5 是模块清单;AppTrackingTransparencyPlugin.ets 是 flutter create 生成的插件模板,只有空壳生命周期方法,真正要写的代码全在这个文件里,2.4 节补全它。pubspec.yaml 自动追加的注册节点:
flutter:
plugin:
platforms:
ios:
pluginClass: AppTrackingTransparencyPlugin
ohos: # flutter create 自动新增
package: com.example.app_tracking_transparency
pluginClass: AppTrackingTransparencyPlugin
flutter create 还会把 Dart SDK 约束放宽一档(上游 >=2.12.0 <3.0.0,生成后为 >=2.12.0 <4.0.0),以兼容当前 ohos 版 SDK 的 Dart 3.11。

图三:命令输出与生成的 ohos 目录
2.4 在插件文件中补全 ohos 实现
先看改动全景。整个适配在插件侧只改了一个 Dart 文件、一个 pubspec 注册节点,外加新增的 ohos 目录:
| 文件 | 改动 | 内容 |
|---|---|---|
lib/app_tracking_transparency.dart | 加法 | 新增 Platform 导入、_isOhos 判断、两个失败语义封装、三个接口的鸿蒙分支 |
pubspec.yaml | flutter create 自动改 | 追加 ohos 注册节点,放宽 SDK 约束 |
ohos/(新增目录) | 加法 | ArkTS 插件 AppTrackingTransparencyPlugin.ets:三个方法各自返回约定常量 |
原有 iOS 平台实现一行不动,全部改动都是加法。
Dart 侧。核心是在三个公开接口入口处加鸿蒙分支,并抽两个私有封装统一失败语义:
import 'dart:io' show Platform;
/// 鸿蒙平台判据:鸿蒙 Flutter 引擎的 Platform.operatingSystem 返回 'ohos'
static bool get _isOhos =>
!kIsWeb && Platform.operatingSystem == 'ohos';
/// 鸿蒙侧统一封装:失败语义与原平台对齐(状态类接口失败返回 notSupported)
static Future<TrackingStatus> _invokeOhosStatus(String method) async {
try {
final int status =
(await _channel.invokeMethod<int>(method))!;
return TrackingStatus.values[status];
} on PlatformException {
return TrackingStatus.notSupported;
}
}
/// 鸿蒙侧统一封装:失败语义与 Android 对齐(广告标识符失败返回空串)
static Future<String> _invokeOhosIdentifier(String method) async {
try {
return (await _channel.invokeMethod<String>(method))!;
} on PlatformException {
return "";
}
}
三个接口的鸿蒙分支——通道与 iOS 完全复用,只是方法调用的目的地从 iOS 原生代码换成了 ArkTS 插件:
static Future<TrackingStatus> get trackingAuthorizationStatus async {
if (_isOhos) {
return _invokeOhosStatus('getTrackingAuthorizationStatus');
}
// ...原有 iOS 分支保持不变...
}
static Future<TrackingStatus> requestTrackingAuthorization() async {
if (_isOhos) {
return _invokeOhosStatus('requestTrackingAuthorization');
}
// ...
}
static Future<String> getAdvertisingIdentifier() async {
if (_isOhos) {
return _invokeOhosIdentifier('getAdvertisingIdentifier');
}
// ...
}
三个实现决策说明:
- 为什么用
Platform.operatingSystem == 'ohos'判断:鸿蒙 Flutter 引擎在Platform.operatingSystem上返回字符串'ohos',比依赖defaultTargetPlatform枚举更稳(后者在部分引擎版本上没有 ohos 取值);配合!kIsWeb排除 Web 端。 - 为什么通道与方法名与 iOS 完全一致:
app_tracking_transparency通道和三个方法名是上游既定契约,ArkTS 插件按同名通道注册即可对接,Dart 侧不需要新通道,跨平台调用方代码零改动。 - 为什么 catch 后返回
notSupported/ 空字符串:状态类接口失败返回notSupported与 iOS 失败路径语义一致;标识符失败返回空串与 Android 的"无标识符"语义一致。即使 ArkTS 侧异常,调用方拿到的也是可预期的返回值,而不是未捕获异常。
ArkTS 侧。flutter create 生成的模板只有空壳,补全后的完整实现:
import {
FlutterPlugin,
FlutterPluginBinding,
MethodCall,
MethodCallHandler,
MethodChannel,
MethodResult,
} from '@ohos/flutter_ohos';
export default class AppTrackingTransparencyPlugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return "AppTrackingTransparencyPlugin"
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), "app_tracking_transparency");
this.channel.setMethodCallHandler(this)
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
if (this.channel != null) {
this.channel.setMethodCallHandler(null)
this.channel = null
}
}
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case 'getTrackingAuthorizationStatus':
// HarmonyOS has no ATT (App Tracking Transparency) concept.
// Per the community adaptation convention, return a fixed
// authorized value: TrackingStatus.authorized is index 3
// of the Dart enum in lib/app_tracking_transparency.dart.
result.success(3);
break;
case 'requestTrackingAuthorization':
// No system dialog is shown; return authorized immediately.
result.success(3);
break;
case 'getAdvertisingIdentifier':
// Aligned with the Android semantics: no advertising
// identifier is available, return an empty string.
result.success("");
break;
default:
result.notImplemented()
}
}
}
关键点说明:
- 生命周期两接口:
onAttachedToEngine用引擎给的BinaryMessenger注册同名通道并挂上 handler;onDetachedFromEngine反注册并置空,避免引擎销毁后悬挂回调。 - 三个方法各拿什么:
getTrackingAuthorizationStatus与requestTrackingAuthorization返回3——这是 Dart 侧TrackingStatus枚举里authorized的下标,ArkTS 侧回传整数、Dart 侧TrackingStatus.values[3]还原,两侧按枚举下标对齐;getAdvertisingIdentifier返回空字符串,与 Android 语义一致。 - 为什么直接同步回值、不调系统能力:鸿蒙没有 ATT 授权弹窗与 IDFA 等价概念,按社区适配约定,状态固定返回
authorized(调用方跨平台逻辑无需写分支),标识符返回空串。没有异步系统调用,也就天然满足MethodResult只许回复一次的约束。 - default 分支必须
notImplemented:未识别的方法名回notImplemented,Dart 侧会抛MissingPluginException语义的异常而不是永久悬挂 Future。
2.5 补全适配说明文件并提交分支
适配完成的仓库还需要四份说明文件,让社区与使用方了解这个鸿蒙版的来龙去脉:
| 文件 | 作用 |
|---|---|
README.OpenSource | 开源登记:名称、协议、版本、上游地址、维护者(社区入库必查) |
README.OpenHarmony.md | 面向社区的英文适配说明 |
README.OpenHarmony_CN.md | 面向社区的中文适配说明 |
CHANGELOG.OpenHarmony.md | 鸿蒙适配版本的变更记录 |
README.OpenSource 实际内容(JSON 数组格式):
[
{
"Name": "app_tracking_transparency",
"License": "MIT License",
"License File": "LICENSE",
"Version Number": "2.0.7",
"Owner": "qiaomu8559968@126.com",
"Upstream URL": "https://github.com/deniza/app_tracking_transparency",
"Description": "A Flutter plugin to display the iOS tracking authorization dialog and request permission to collect data. This repository adds OpenHarmony platform support on top of the original library."
}
]
CHANGELOG.OpenHarmony.md 实际内容:
# 变更记录
## [2.0.7-ohos-1.0.0-beta.1]
- 适配 OpenHarmony 平台,新增 ohos 目录与 Dart 侧平台分支
- 依赖 ohos 版 Flutter SDK 3.41.10-ohos-1.0.1
- 模拟器验证环境:HarmonyOS Emulator(HarmonyOS 7.0.0.105 / API 26)
提交适配 commit 并打 TAG。TAG 命名规则是 原库版本-ohos-适配版本-beta.x,原库版本 2.0.7,首个鸿蒙适配版本即 2.0.7-ohos-1.0.0-beta.1:
git add .
git commit -m "feat: adapt app_tracking_transparency for the OpenHarmony platform"
git push atomgit feat/ohos_app_tracking_transparency_2.0.7
# 打 TAG 并推送
git tag 2.0.7-ohos-1.0.0-beta.1
git push atomgit 2.0.7-ohos-1.0.0-beta.1


图四:适配 commit 与 TAG 的提交记录
三、在 Demo 中验证适配效果
3.1 使用仓库自带的 example
上游自带的 example 是最合适的验证载体,改造方式是把示例页重做成三接口演示页:三个操作卡片分别触发 trackingAuthorizationStatus、requestTrackingAuthorization()、getAdvertisingIdentifier(),页面下方用调用日志区实时打印每次调用的返回值,另加一张平台说明卡标注鸿蒙侧语义。演示页改造单独成 commit:
git commit -m "feat: rework example app as a dedicated app_tracking_transparency demo for OpenHarmony"
构建与安装(签名配置的实踩见 4.1 的 Q2):
cd example
flutter pub get
flutter build hap --debug
# 产物在 example/ohos/entry/build/default/outputs/default/ 下
hdc install -r entry-default-signed.hap
# bundleName 见 example/ohos/AppScope/app.json5
hdc shell aa start -a EntryAbility -b com.he2apps.app_tracking_transparency_example
应用启动后的主界面——initState 里自动查询了一次授权状态,日志区已经能看到第一条返回:

图五:example 在鸿蒙模拟器上的主界面
3.2 自建工程时以 AtomGit 链接方式引入
不改示例、直接在自己的工程里用时,pubspec 用 git 依赖并锁定 TAG:
dependencies:
app_tracking_transparency:
git:
url: https://atomgit.com/oh-flutter/app_tracking_transparency.git
ref: 2.0.7-ohos-1.0.0-beta.1
然后 flutter pub get 即可。TAG 与框架版本对照:
| 项 | 值 |
|---|---|
| TAG | 2.0.7-ohos-1.0.0-beta.1 |
| 分支 | feat/ohos_app_tracking_transparency_2.0.7 |
| 实测框架 | Flutter 3.41.10-ohos-1.0.1(stable) |
兼容性说明:本文在 stable(3.41.10-ohos-1.0.1)实测通过;canary 引擎对宿主工程 compatibleSdkVersion 有额外要求,使用时以社区环境指南为准。
3.3 调用接口并观察模拟器运行效果
最小调用代码就是三个静态接口,逐一在模拟器上验证。
接口一:查询授权状态。点击"查询授权状态"卡片:
final TrackingStatus status =
await AppTrackingTransparency.trackingAuthorizationStatus;
日志区打印 getTrackingAuthorizationStatus → authorized (index 3),返回的是枚举 authorized:

图六:查询授权状态返回 authorized(index 3)
接口二:请求跟踪授权。点击"请求跟踪授权"卡片:
final TrackingStatus status =
await AppTrackingTransparency.requestTrackingAuthorization();
日志区打印 requestTrackingAuthorization → authorized (index 3)。与 iOS 不同,鸿蒙上没有系统弹窗,调用立即返回授权结果——这正是适配约定的语义:调用方的后续逻辑(授权通过才采集)不需要写平台分支:

图七:请求授权无弹窗、直接返回 authorized(index 3)
接口三:获取广告标识符。点击"获取广告标识符"卡片:
final String id = await AppTrackingTransparency.getAdvertisingIdentifier();
日志区打印 getAdvertisingIdentifier → "<空字符串>"。与 Android 一致,鸿蒙上没有 IDFA 等价物,返回空字符串:

图八:获取广告标识符返回空字符串
适配完成度总结:三个接口在鸿蒙上全部全链路可用;授权状态与请求授权两个接口固定返回 authorized(受生态制约,鸿蒙无 ATT 概念,但语义正确且跨平台逻辑无需分支);广告标识符返回空字符串,与 Android 各平台行为一致。所有返回值都有明确定义,调用方可以按统一逻辑处理。
四、常见问题
4.1 适配过程中的问题
Q1:flutter create --platforms ohos . 之后跑 flutter analyze 报一堆 sdk_version_since、super-parameters?
这是上游 example 的 SDK 约束太老导致的:example/pubspec.yaml 里是 sdk: ">=2.12.0 <3.0.0",而 ohos 版 SDK 的 Dart 3.11 analyzer 会按 3.x 语言版本做检查,老约束下新语法告警全冒出来。把 example 的约束升到 >=3.0.0 <4.0.0 后 analyze 即通过。主库的约束 flutter create 已自动放宽为 >=2.12.0 <4.0.0,无需手动改。
Q2:flutter build hap 提示"请通过 DevEco Studio 打开 ohos 工程后配置调试签名"?
构建签名 HAP 需要证书与 Profile。实踩步骤:先拿设备 UDID——hdc shell bm get --udid,注意输出带 udidofcurrentdeviceis: 前缀,要清洗后再用;然后到 ~/.ohos/config/ 下找 DevEco 自动签名生成的证书组(default_<项目名>_<hash>.cer/.p12/.p7b),Profile(p7b)里绑定了 bundleName 与设备 UDID,选匹配的那组;最后把签名配置临时注入 example/ohos/build-profile.json5(加密后的 storePassword/keyPassword 串在同一台机器上跨项目可用),或直接用 DevEco Studio 打开 example/ohos 工程走 File > Project Structure 的自动签名。签名配置不入库,提交前记得还原 build-profile.json5 与 app.json5。
Q3:运行时报 MissingPluginException,怎么排查?
三处按序检查:pubspec.yaml 的 plugin.platforms 下有没有 ohos 节点(没有就重跑 flutter create --platforms ohos .);ArkTS 侧注册的通道名与 Dart 侧 MethodChannel('app_tracking_transparency') 是否完全一致;改完有没有重新 flutter pub get 并重新构建 HAP(热重载不会重装插件)。
Q4:ArkTS 侧 MethodResult 有什么使用禁忌?
每个 onMethodCall 对每个调用只允许 result.success/failure/notImplemented 之一执行一次,重复回复会崩溃。本库三个方法都是同步返回常量,天然满足;如果适配的是有异步回调的系统能力(如订阅类接口),要加"只回一次"的标志位保护。
4.2 使用过程中的问题
Q1:鸿蒙上为什么永远返回 authorized,也不弹授权弹窗?
ATT(App Tracking Transparency)是 iOS 14+ 的隐私机制,鸿蒙没有对应概念。按社区适配约定,鸿蒙侧固定返回 authorized(枚举下标 3),且不弹任何弹窗。这样"先请求授权、通过后才采集"的跨平台代码在鸿蒙上可以直接走通,不需要 Platform.isIOS 分支。
Q2:鸿蒙上 getAdvertisingIdentifier() 为什么返回空字符串?
与 Android 的语义对齐:Android 上该接口本来就返回空字符串(上游 Dart 侧兜底),鸿蒙侧遵循同样约定。如果业务在鸿蒙上确实需要广告标识符(OAID),应接入鸿蒙系统广告服务相关接口,那不在本库职责范围内。
Q3:Android 上调用是什么行为?会不会和鸿蒙不一致?
一致。Android 上状态类接口返回 notSupported、标识符返回空字符串;鸿蒙上状态类接口返回 authorized(比 Android 的 notSupported 更"可用",这是刻意的语义选择,让合规采集逻辑在鸿蒙可用)、标识符返回空字符串。跨平台判断请以 TrackingStatus 枚举值而非平台判断来分流。
五、结语
回顾整个链路:同步上游到 AtomGit、clone 到宿主机、建分支并 flutter create --platforms ohos 补全目录、Dart 与 ArkTS 两侧补实现、补齐四份说明文件、提交分支与 TAG,最后在 example 里逐接口验证。插件使用中发现接口行为问题,请到鸿蒙仓库 Issue反馈(管适配层);原库本身的逻辑问题请到上游仓库 Issue反馈。接口用法与业务实战见姊妹篇《给鸿蒙 App 增加跟踪授权与广告标识符查询能力 —— app_tracking_transparency 的鸿蒙使用指南》。
相关链接
欢迎加入 CPF-Flutter 鸿蒙社区,社区入口、环境搭建指南和本文相关链接统一放在这里:
- CPF-Flutter 鸿蒙社区
- Flutter OHOS 开发环境搭建指南
- 鸿蒙版 app_tracking_transparency 仓库
- 本文 TAG:
2.0.7-ohos-1.0.0-beta.1(分支feat/ohos_app_tracking_transparency_2.0.7) - example 演示工程
- 上游仓库(GitHub)
- pub.dev 包页
更多推荐




所有评论(0)