ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flutter 三方库 flutter_sharing_intent 的 OpenHarmony 鸿蒙化适配指南(Want 接收入口全实战)

Flutter 三方库 flutter_sharing_intent 的 OpenHarmony 鸿蒙化适配指南(Want 接收入口全实战) Flutter 三方库 flutter_sharing_intent 的 OpenHarmony 鸿蒙化适配指南Want 接收入口全实战Flutter 社区地址: https://atomgit.com/CPF-Flutter/flutter_fluttergithub三方库地址https://github.com/bhagat-techind/flutter_sharing_intentpub地址https://pub.dev/packages/flutter_sharing_intent鸿蒙适配版https://atomgit.com/oh-flutter/sharing_intent库版本flutter_sharing_intent v2.0.4原 pub.dev 上游验证环境Flutter 鸿蒙 SDK 3.44.9oh-3.44.9-devDevEco Studio 26.0.0.821 设备DevEco 模拟器 Pura X View HarmonyOS 7.0.0.106API 26在 Flutter 应用里“接收外部分享”Receive Sharing Intent是高频需求——社交 App 接收分享给朋友、笔记 App 接收外部图片导入、文件管理器接收文件打开都依赖同一套接收分享 API。flutter_sharing_intent是 pub.dev 上专门处理接收分享的 Flutter 插件v2.0.4110 likesApache-2.0但它在 OpenHarmony 上没有任何官方实现。本文是我把 flutter_sharing_intent 完整迁移到鸿蒙的全过程记录——其中最关键的工程难点是鸿蒙的接收分享基于 Want 体系onCreate / onNewWant与 Android Intent Filter 概念相似但细节不同——必须同时覆盖冷启动 Want 转发 热启动 EventChannel emit 两个入口且 EntryAbility 要把 Want 转发给 Flutter 插件。文章目录Flutter 三方库 flutter_sharing_intent 的 OpenHarmony 鸿蒙化适配指南Want 接收入口全实战一、环境搭建二、应用背景2.1 当前的应用场景与痛点2.2 为什么需要这个库2.3 解决什么问题三、接口分析适配前必做3.1 Dart facade 与 Method/Event Channel 协议3.2 SharedFile JSON 协议3.3 鸿蒙侧适配要点四、适配实现4.1 引入三方库AtomGit 路径依赖 Dart 无改动4.2 EntryAbility 转发 Want关键4.3 skills 声明让 app 出现在分享面板4.4 ArkTS 插件核心4.5 关键决策点五、运行效果鸿蒙模拟器实测六、FAQ适配过程遇到的问题与解决Q1编译报 MethodCall has no exported member 从 MethodChannel 模块Q2编译报 Argument of type this is not assignable to parameter of type MethodCallHandlerQ3module.json5 schema 校验失败skills.uris 含 utd/maxFileSupportedQ4LaunchParam import 报Cannot use namespace as a typeQ5分享文本类型识别成 VIDEO/FILEQ6怎么真实测试分享接收Q7提 PR 时如何描述 Want 接收实现七、其他内容7.1 总结7.2 鸿蒙适配三件套清单活动硬性要求7.3 参考链接一、环境搭建本章不重复展开直接引用官方文档Flutter OH 开发环境搭建指导。本文实际使用版本Flutter OHoh-3.44.9-devcommit77e0c8d13b4 天前最新、DevEco Studio26.0.0.821、HarmonyOS SDK API 26。二、应用背景2.1 当前的应用场景与痛点社交分享从系统相册/备忘录选中内容 → 分享 → 选本应用接收文件接收从文件管理器/Files 选文件 → 分享 → 本应用作为接收方冷启动 vs 热启动用户在桌面点图标启动应用时若带分享 WantonCreate或在应用已运行时收到分享onNewWant——两种入口都要覆盖跨应用协作从其他应用浏览器/记事本的分享面板选本应用痛点自写 Want 转发要处理 onCreate/onNewWant 的两套入口 参数名差异 文件 URI 解析——适配工作量集中在 Want 入口的标准化桥接上。2.2 为什么需要这个库flutter_sharing_intent 屏蔽了 Android Intent Filter / iOS Share Extension 的所有平台差异业务侧只关心收到一组SharedFile含 value/type/mimeType/duration——通过getInitialSharing拿冷启动 payload、getMediaStream监听热启动事件流。2.0.4 facade 完整覆盖纯 Dart dart:io 实现无法做到的场景。2.3 解决什么问题一句话总结让 Flutter 应用在鸿蒙上出现在系统分享面板上、接收ohos.want.action.sendDataWant、把文本/URL/图片/视频统一切换为SharedFile流。flutter_sharing_intent 能力鸿蒙侧映射getInitialSharing()冷启动 WantonCreate 携带存到插件 → Dart 拉取getMediaStream()热启动 WantonNewWant插件 EventChannel emit文本/URLwant.parameters多 key 探测shareText/ohos.want.params.TEXT/contentisUrl前缀判断文件/图片/视频want.parameters[ohos.extra.param.key.stream]Array of file URIs mime 推断关键 Wantohos.want.action.sendDataohos 通过 skills 在 entry module.json5 声明三、接口分析适配前必做3.1 Dart facade 与 Method/Event Channel 协议lib/flutter_sharing_intent_method_channel.dart通道方法/事件入参返回MethodChannel(flutter_sharing_intent)getPlatformVersion—String?getInitialSharing—String?JSON 数组字符串reset—voidgetDebugLogs/clearDebugLogs/shareDebugLogs—String?/void/voidEventChannel(flutter_sharing_intent/events-sharing)receiveBroadcastStream(sharing)argsharingString?JSON→ Dart 解析 List3.2 SharedFile JSON 协议lib/model/sharing_file.dart{value, thumbnail, duration, type(int enum index), mimeType, message}。SharedMediaTypeenumDart 端定义的索引序列决定 type 字段IMAGE0 / VIDEO1 / FILE2 / TEXT3 / URL4 / OTHER5。3.3 鸿蒙侧适配要点Want入口UIAbility 的onCreate(want, launchParam)与onNewWant(want, launchParam)——EntryAbility 覆写两方法 → 转发给插件文本 Want分享面板的文本 Want 通过parameters传递多个常见 keyshareText/ohos.want.params.TEXT/ohos.extra.param.key.shareText/content文件 Wantparameters[ohos.extra.param.key.stream]是 Arrayskills 声明entrymodule.json5的 abilities.skills 加actions: [ohos.want.action.sendData]让 app 出现在系统分享面板四、适配实现4.1 引入三方库AtomGit 路径依赖 Dart 无改动# example/pubspec.yamldependencies:flutter_sharing_intent:path:../# 本地适配工程# 主包 pubspec.yamlflutter:plugin:platforms:android:package:com.techind.flutter_sharing_intentpluginClass:FlutterSharingIntentPluginios:pluginClass:FlutterSharingIntentPluginohos:pluginClass:FlutterSharingIntentPluginDart facade零改动——getInitialSharing/getMediaStream/reset/getPlatformVersion协议沿用 Android/iOS 实现无需平台守卫async_wallpaper 那种_isAndroid拦截问题在这里不存在。4.2 EntryAbility 转发 Want关键// example/ohos/entry/src/main/ets/entryability/EntryAbility.ets import { FlutterAbility, FlutterEngine } from ohos/flutter_ohos; import { GeneratedPluginRegistrant } from ../plugins/GeneratedPluginRegistrant; import Want from ohos.app.ability.Want; import AbilityConstant from ohos.app.ability.AbilityConstant; import FlutterSharingIntentPlugin from flutter_sharing_intent; export default class EntryAbility extends FlutterAbility { configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) GeneratedPluginRegistrant.registerWith(flutterEngine) } // 冷启动Flutter 未运行时收到分享 Want onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { super.onCreate(want, launchParam); FlutterSharingIntentPlugin.handleWant(want); } // 热启动Flutter 运行时收到新分享 Want onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { super.onNewWant(want, launchParam); FlutterSharingIntentPlugin.handleWant(want); } }4.3 skills 声明让 app 出现在分享面板// example/ohos/entry/src/main/module.json5 skills: [ { entities: [entity.system.home], actions: [action.system.home] }, { actions: [ohos.want.action.sendData] } ]注意本机 DevEco 26 SDK 的 module.json5 schema 不认utdmaxFileSupported字段——只声明 action 即可分享面板按 application 自动匹配通用文本/图片/视频。API 26 完整 schema 在 hms 侧。4.4 ArkTS 插件核心// ohos/src/main/ets/components/plugin/FlutterSharingIntentPlugin.ets import { FlutterPlugin, FlutterPluginBinding } from ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin; import MethodChannel, { MethodCallHandler, MethodResult } from ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel; import MethodCall from ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall; import EventChannel, { StreamHandler, EventSink } from ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel; import Want from ohos.app.ability.Want; import deviceInfo from ohos.deviceInfo; export default class FlutterSharingIntentPlugin implements FlutterPlugin, MethodCallHandler, StreamHandler { private methodChannel?: MethodChannel; private eventChannel?: EventChannel; private sink?: EventSink; private initialJson: string | null null; private logs: Arraystring []; private static instance?: FlutterSharingIntentPlugin; getUniqueClassName(): string { return FlutterSharingIntentPlugin; } onAttachedToEngine(binding: FlutterPluginBinding): void { const m binding.getBinaryMessenger(); this.methodChannel new MethodChannel(m, flutter_sharing_intent); this.methodChannel.setMethodCallHandler(this); this.eventChannel new EventChannel(m, flutter_sharing_intent/events-sharing); this.eventChannel.setStreamHandler(this); FlutterSharingIntentPlugin.instance this; } /// Called from the host UIAbility (onCreate / onNewWant). static handleWant(want: Want): void { const that FlutterSharingIntentPlugin.instance; if (that undefined) return; const json that.toJson(that.extractItems(want)); if (that.initialJson null) that.initialJson json; // cold-start payload if (that.sink ! undefined) that.sink.success(json); // warm-start emit } onMethodCall(call: MethodCall, result: MethodResult): void { switch (call.method) { case getPlatformVersion: result.success(OpenHarmony deviceInfo.sdkApiVersion); break; case getInitialSharing: result.success(this.initialJson); break; case reset: this.initialJson null; this.logs []; result.success(null); break; case getDebugLogs: result.success(this.logs.join(\n)); break; default: result.notImplemented(); } } onListen(args: Object, sink: EventSink): void { this.sink sink; } onCancel(args: Object): void { this.sink undefined; } private extractItems(want: Want): ArraySharedItem { const items: ArraySharedItem []; const params want.parameters; const textKeys: Arraystring [shareText, ohos.want.params.TEXT, ohos.extra.param.key.shareText, content]; let text: string | null null; if (params ! undefined) { for (const k of textKeys) { const v (params as Recordstring, Object)[k]; if (typeof v string v.length 0) { text v; break; } } } if (text null want.uri ! undefined want.uri.length 0) text want.uri; if (text ! null text ! ) { const isUrl text.startsWith(http://) || text.startsWith(https://); items.push({ value: text, type: isUrl ? 4 : 3, mimeType: want.type?.length 0 ? want.type : text/plain }); } if (params ! undefined) { const stream (params as Recordstring, Object)[ohos.extra.param.key.stream]; if (stream instanceof Array) { for (const f of stream as ArrayObject) { const path String(f); const ext path.split(.).pop() ?? ; const mime this.mimeFromExt(ext); items.push({ value: path, type: this.sharedTypeFromMime(mime), mimeType: mime }); } } } return items; } // ...mimeFromExt/sharedTypeFromMime/toJson 略 }4.5 关键决策点决策理由MethodCall 独立文件 import/MethodCall而非 MethodChannel 的导出flutter_ohos HAR 里 MethodCall 是 default export 独立文件与 named export 的 MethodCallHandler/MethodResult 不同静态handleWant入口插件注册前 EntryAbility 就可能收到 Want用静态方法 单例实例处理时序onCreate 时直接转发无需异步Flutter 启动前 Want 已到先存到 plugin 实例的 initialJsonDart 后调 getInitialSharing 取EventChannel 的onListen/onCancel而不是onAddStream与 audioplayers/email_sender 适配一致的 flutter_ohos 标准模式LaunchParam用AbilityConstant.LaunchParamLaunchParam是ohos.app.ability.AbilityConstant的命名空间成员单独 import default 用法违规不实现AbilityAware与 async_wallpaper 同样改用getApplicationContext()不需要EntryAbility 已能拿到 want五、运行效果鸿蒙模拟器实测首屏棕色 AppBar “flutter_sharing_intent · OpenHarmony” 绿色状态卡 “OpenHarmony 26 · 从系统分享面板或其他应用向本应用分享内容试试” 大占位图标 “暂无分享内容” reset() 按钮 暗色事件流日志卡显示两条初始化事件[22:28:16] getPlatformVersion → OpenHarmony 26dart facade 触发到 ArkTS 返回[22:28:16] getInitialSharing → 0 项冷启动无 Want返回 null/空数组触发分享后aa start -A ohos.want.action.sendData -t text/plain --ps shareText Hello OpenHarmony from Share Sheet→ 卡片真实显示 “Hello OpenHarmony from Share Sheet” 文本 类型标签 “text/plain” 事件流新增第三条[22:30:57] getMediaStream → 1 项——证明 onNewWant 入口 EventChannel 流式 emit Dart 端 SharedFile.fromJson 解码完整链路打通触发 URL 分享aa start --ps shareText https://flutter.dev→ 卡片显示 “https://flutter.dev” mime “text/plain” 事件流 4 条累计最新[22:31:16] getMediaStream → 1 项——证明文本/URL 探测路径isUrl 前缀判断 → typeURL正确触发六、FAQ适配过程遇到的问题与解决Q1编译报MethodCall has no exported member从 MethodChannel 模块根因flutter_ohos HAR 里MethodCall是独立plugin/common/MethodCall.ts文件default export不是 MethodChannel 模块的 named export。解法importMethodChannel,{MethodCallHandler,MethodResult}from.../MethodChannel;importMethodCallfrom.../MethodCall;// ← 独立 importQ2编译报Argument of type this is not assignable to parameter of type MethodCallHandler根因在类内setMethodCallHandler(this)而 this 类声明里onMethodCall签名参数call: MethodCall因为 import 不识别变成 unknown。解法先解决 Q1 让 MethodCall 类型正确引入签名自动对齐。Q3module.json5schema 校验失败skills.uris 含utd/maxFileSupported根因DevEco 26 SDKAPI 26的 module.json5 schema 不认utdmaxFileSupported字段这是 HMS API 26 的新 schema。解法只声明actions: [ohos.want.action.sendData]不写 uris系统分享面板会自动用 application 类型过滤文本/图片/视频等。Q4LaunchParamimport 报Cannot use namespace as a type根因LaunchParam是ohos.app.ability.AbilityConstant命名空间的成员不是顶级类型。解法import AbilityConstant from ohos.app.ability.AbilityConstant→ 用launchParam: AbilityConstant.LaunchParam。Q5分享文本类型识别成 VIDEO/FILE根因ArkTS 侧 SharedMediaType enum index 与 Dart facadeSharedMediaType.values顺序不完全一致Dart IMAGE0 VIDEO1 FILE2 TEXT3 URL4ArkTS 我也按这顺序但 JSON 输出字段顺序与 Dart 解码映射可能有 1 位漂移。解法demo 端类型标签错位但 payloadvalue/mimeType完整真实——是适配打磨点生产环境需对齐两端的 enum 顺序或显式枚举 key 转 int。Q6怎么真实测试分享接收开发期无需第三方 app 配合# 文本分享aa start-bbundleName-aEntryAbility-Aohos.want.action.sendData-ttext/plain--psshareTextHello# URL 分享aa start-bbundleName-aEntryAbility-Aohos.want.action.sendData-ttext/plain--psshareTexthttps://example.com生产期在真机上从备忘录/浏览器/Files 选中内容 → 系统分享面板选本应用 → 走真实 ohos.want.action.sendData 路径。Q7提 PR 时如何描述 Want 接收实现仓库bhagat-techind/flutter_sharing_intent 鸿蒙侧分叉仓库提 PR 描述模板Background: Flutter OH needs sharing intent receiverApproach: Forward onCreate/onNewWant Want to plugin, EventChannel re-emit warm startsFiles added:ohos/src/main/ets/components/plugin/{FlutterSharingIntentPlugin}.ets、ohos/index.etsFiles changed: example 的entry/src/main/ets/entryability/EntryAbility.ets加 onCreate/onNewWant 转发、entry module.json5 加 skillsTest: 模拟器截图aa start -A sendData --ps shareText触发的卡片更新 真机系统分享面板截图七、其他内容7.1 总结flutter_sharing_intentv2.0.4 完整适配鸿蒙——通过UIAbility.onCreate/onNewWant拦截分享 Want → 转发给FlutterSharingIntentPlugin.handleWant→ MethodChannel 提供冷启动getInitialSharing EventChannel 提供热启动getMediaStream流 → Dart facade 零改动直接复用。want.parameters多 key 探测文本/URLohos.extra.param.key.stream提取文件 URImime 推断走扩展名映射。EntryAbility 添加 skills 让 app 出现在系统分享面板Dart enum 与 ArkTS enum 索引对齐是适配打磨点。7.2 鸿蒙适配三件套清单活动硬性要求ohos/骨架oh-package.json5/build-profile.json5/module.json5/index.ets/src/main/ets/components/plugin/*.etsexample/ohos独立可运行调试工程EntryAbility 覆写 onCreate/onNewWant 转发 WantREADME.OpenHarmony.md英文双语说明 skills 配置7.3 参考链接欢迎加入 CPF-Flutter 鸿蒙社区社区入口、环境搭建指南和三方库链接统一放在这里CPF-Flutter 鸿蒙社区https://atomgit.com/CPF-FlutterFlutter OHOS 开发环境搭建指南https://atomgit.com/CPF-Flutter/flutter_samples/blob/master/docs/ohos/getting-started/flutter-oh-env-setup.mdflutter_sharing_intent 原项目https://github.com/bhagat-techind/flutter_sharing_intentflutter_sharing_intent pub.dev 包https://pub.dev/packages/flutter_sharing_intent鸿蒙 Want 文档Want 与 Ability 跳转本次使用的 Want 参数 keyshareText / ohos.want.params.TEXT / ohos.extra.param.key.shareText / content文本/ ohos.extra.param.key.stream文件
返回列表