Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 让 launch_app_store 打开 AppGallery
Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 让 launch_app_store 打开 AppGallery
这篇文章记录一个很适合新手理解的 Flutter 鸿蒙适配过程:原插件的目标是打开 Google Play 或 Apple App Store,而鸿蒙设备没有这两个商店。我们不把 Android 的 Intent 代码硬搬到 ArkTS,而是把同一个 Dart launch() API 映射为鸿蒙的应用市场搜索入口。
本文使用 launch_app_store 2.0.0、Flutter OH 3.47.4-ohos-1.0.4 和 HarmonyOS API 26,从代码分析、平台注册、Ability 获取、Want 跳转、HAP 构建、签名、安装到真机回退,完整走一遍。
一、最终效果
图 1:真机启动适配后的 Demo。
图 2:点击按钮后进入 AppGallery 相关页面,说明鸿蒙 Ability 跳转已经生效。
图 3:从应用市场返回 Flutter 页面,Dart 层显示“调用完成”。
| 验证点 | 真机结果 |
|---|---|
| Flutter | 3.47.4-ohos-1.0.4 |
| HarmonyOS | 7.0.0.105,API 26 |
| HAP | signed HAP 构建通过 |
| 点击调用 | Flutter 页面调用 LaunchAppStore.launch() |
| 系统跳转 | 进入 AppGallery 相关页面 |
| 返回应用 | Flutter 页面显示调用完成 |
二、先理解原插件做什么
原库的公开 API 只有一个核心入口:
await LaunchAppStore.launch(
androidAppId: 'com.jbferreira.kural',
iOSAppId: '585027354',
);
参数含义:
| 参数 | 原平台用途 | 鸿蒙处理 |
|---|---|---|
androidAppId | Google Play 包名 | 作为 AppGallery 搜索关键词 |
iOSAppId | App Store 数字 ID | 鸿蒙端不使用 |
writeReview | 是否打开评论入口 | 鸿蒙端不使用 |
showToast | Android/iOS 提示 | 鸿蒙端不使用 |
toastMessage | 提示文字 | 鸿蒙端不使用 |
适配时最重要的判断是:鸿蒙不能保证 Android 包名一定对应 AppGallery 详情页。因此本次使用 AppGallery 搜索地址,把包名作为关键词传过去。这样不会伪造一个不存在的详情链接,用户仍然可以在应用市场中继续查找。
三、准备 Flutter 3.47 和鸿蒙设备
本文实际环境:
| 工具 | 版本 |
|---|---|
| Flutter OH | 3.47.4-ohos-1.0.4 |
| Dart | 3.13.3 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26 |
| 测试设备 | 127.0.0.1:5555,HarmonyOS 7.0.0.105 |
没有 Flutter 鸿蒙环境时,先看 Flutter OH 环境搭建指南。
export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter --version
hdc list targets
确认版本和设备后再继续。公开文章截图中不要出现完整设备序列号、证书路径和密码。
四、拉取代码和核对适配分支
本文使用的 AtomGit 信息:
- 仓库:https://atomgit.com/oh-flutter/launch_app_store
- 适配分支:feat/ohos_launch_app_store_2.0.0
- 受测提交:098d4d598c26b8038f19d4399fc95f64b18da545
执行:
git clone https://atomgit.com/oh-flutter/launch_app_store.git
cd launch_app_store
git switch feat/ohos_launch_app_store_2.0.0
git log -1 --oneline
这里要特别注意仓库目录名和 Dart 包名都是 launch_app_store,但包的上游主页仍然指向 launch_review。写文章时要把“包名、仓库名、上游地址”分别写清楚,避免读者搜索不到代码。
五、先分析原来的调用链
上游 Dart 代码使用固定通道:
static const MethodChannel _channel =
MethodChannel('launch_app_store');
调用时发送:
await _channel.invokeMethod('launch', {
'android_id': androidAppId,
'ios_id': iOSAppId,
'write_review': writeReview,
'is_ios_beta': isiOSBeta,
'toast_message': toastMessage,
'show_toast': showToast,
});
原 Android 实现会根据包名拼接 Google Play 地址,iOS 实现会拼接 App Store 地址。鸿蒙适配不能继续调用这两个商店,所以保留通道名和方法名,只替换接收端行为:
Flutter LaunchAppStore.launch()
-> MethodChannel('launch_app_store')
-> LaunchAppStorePlugin.onMethodCall()
-> UIAbilityContext.startAbility()
-> AppGallery 搜索页
这样业务代码不用因为平台变化而分成三套调用方式。
六、声明 OHOS 插件平台
打开 pubspec.yaml,在 Android 和 iOS 平台旁边增加:
flutter:
plugin:
platforms:
android:
package: com.jbferreira.launchappstore
pluginClass: LaunchAppStorePlugin
ohos:
pluginClass: LaunchAppStorePlugin
ios:
pluginClass: LaunchAppStorePlugin
pluginClass 对应:
ohos/src/main/ets/components/plugin/LaunchAppStorePlugin.ets
入口文件:
import LaunchAppStorePlugin from './src/main/ets/components/plugin/LaunchAppStorePlugin';
export default LaunchAppStorePlugin;
到这一步只完成了“让 Flutter 知道有 OHOS 插件”。真正的系统跳转还没有实现。
七、为什么需要 AbilityAware
Flutter 插件的 MethodChannel 可以收到 Dart 调用,但 startAbility() 需要当前前台 UIAbility 的上下文。插件不能直接使用一个全局 context,否则页面切换后可能还拿着旧 Ability。
ArkTS 类增加 AbilityAware:
export default class LaunchAppStorePlugin
implements FlutterPlugin, MethodCallHandler, AbilityAware {
private ability: UIAbility | null = null;
onAttachedToAbility(binding: AbilityPluginBinding): void {
this.ability = binding.getAbility();
}
onDetachedFromAbility(): void {
this.ability = null;
}
}
调用时先检查:
if (this.ability === null) {
result.error(
'NO_ABILITY',
'A foreground UIAbility is required.',
null,
);
return;
}
这一步对新手很重要:如果不做生命周期判断,首次 Demo 可能能跑,页面重建、后台返回或多引擎场景就容易出现空指针。
八、把包名转换为 AppGallery 搜索地址
鸿蒙端接收到 Dart 参数后读取 android_id:
const packageName = call.argument('android_id') as string | undefined;
const keyword = packageName && packageName.length > 0
? packageName
: '应用市场';
const uri =
`https://appgallery.huawei.com/search?keyword=${encodeURIComponent(keyword)}`;
这里没有把 Android 包名直接拼成一个假详情页,而是放进搜索参数。原因有两个:
- 同一个包名不一定在 AppGallery 上架。
- 不同地区的应用市场页面和详情 URL 可能不同。
最后使用当前 Ability 的 context 打开 URI:
try {
await this.ability.context.startAbility({ uri: uri });
result.success(null);
} catch (error) {
result.error('START_ABILITY_FAILED', `${error}`, null);
}
如果设备没有可处理该 URI 的 Ability,Dart 层会收到 PlatformException,Demo 会把错误码和错误信息显示出来。
九、准备新手 Demo
Demo 页面只保留一个按钮,避免把原 Android/iOS 的无关参数全部堆到页面上:
Future<void> _launch() async {
try {
await LaunchAppStore.launch(
androidAppId: 'com.jbferreira.kural',
);
if (!mounted) return;
setState(() {
_status = '调用完成';
_detail = '平台已处理应用市场入口';
});
} on PlatformException catch (error) {
if (!mounted) return;
setState(() {
_status = error.code;
_detail = error.message ?? '当前平台没有对应能力';
});
}
}
这个 Demo 的判断标准不是“按钮点击后页面一定停留在 Flutter”。打开系统应用市场后,Flutter Activity 可能退到后台;返回 Flutter 后再检查页面状态,才能证明调用链完整结束。
十、构建和签名
先进行无签名构建:
flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign
需要安装真机时,在 DevEco Studio 中打开 example/ohos,进入 File -> Project Structure -> Signing Configs,勾选自动生成调试签名:
flutter build hap --debug
安装:
hdc list targets
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo
本文没有把签名证书和密码提交到仓库。读者如果使用自己的包名,需要同步修改 AppScope/app.json5 中的 bundleName,并重新生成签名。
十一、真机验证步骤
启动 Demo 后按顺序操作:
- 确认首页显示“尚未调用”。
- 点击“尝试打开应用市场”。
- 确认系统进入 AppGallery 相关页面。
- 按返回键回到 Flutter 页面。
- 确认页面显示“调用完成”。
真机结果:AppGallery UIExtension 进入前台,返回 Flutter 后页面显示“调用完成”。这证明 startAbility() 成功处理了 URI,Dart Future 也完成了返回。
如果设备没有可用网络,AppGallery 页面可能只显示空白或加载状态,但这不影响对“是否调用了 Ability”的判断。文章中应该把“Ability 启动成功”和“应用市场网络内容加载成功”分开记录。
十二、常见问题
Q1:点击后没有离开 Flutter 页面
先检查设备是否在线,以及 aa dump -a 中是否出现应用市场相关 Ability。再确认 URI 是 https://,不要把一个空字符串传给 startAbility()。
Q2:为什么不直接打开 Android 应用详情页
因为 Android 包名不保证在 AppGallery 有对应详情页。搜索页可以让用户继续查找,适配结果更可靠。
Q3:为什么回到 Flutter 后状态显示“调用完成”
startAbility() 返回成功只代表系统接受了 URI 并启动了目标 Ability,不代表目标页面内的网络内容一定加载完成。Demo 的“调用完成”只描述系统跳转成功。
Q4:为什么需要 AbilityAware
插件生命周期可能早于或晚于 UIAbility。保存并清理当前 Ability,可以避免页面切换后使用失效 context。
Q5:构建提示兼容 SDK 不足
Flutter 3.47 的 @ohos/flutter_ohos 依赖要求较高兼容版本。把 example/ohos/build-profile.json5 的 compatibleSdkVersion 和 targetSdkVersion 对齐到 API 26,再重新构建。
十三、复现命令
git clone https://atomgit.com/oh-flutter/launch_app_store.git
cd launch_app_store
git switch feat/ohos_launch_app_store_2.0.0
export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign
十四、总结
这次适配改动了三层:pubspec.yaml 注册 OHOS,Dart 保持原有 launch() 通道,ArkTS 使用当前 UIAbility 的 context 调用 startAbility()。真正需要新手关注的是适配过程中的语义选择:Android 包名不能直接当成鸿蒙详情页 URL,应该转换为 AppGallery 搜索关键词;系统跳转成功也不能等同于网络页面加载成功。
十五、参考资料与仓库地址
本文适配仓库
上游和包信息
Flutter 和鸿蒙资料
- Flutter 官方插件开发文档
- Flutter MethodChannel 文档
- Flutter PlatformInterface 文档
- Flutter OH 环境搭建指南
- HarmonyOS 开发者官网
- HarmonyOS ArkTS 文档
- HarmonyOS Ability 文档
- HarmonyOS Want 文档
- HarmonyOS 应用签名和调试文档
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐




所有评论(0)