Flutter 鸿蒙实战:用 share_plus 三方库给应用接一个系统分享面板
欢迎加入 CPF-Flutter 鸿蒙社区: CPF-Flutter - 开源代码托管,代码协作 - AtomGit
写完一篇攻略、挑好一件商品,想让用户顺手把它转到微信、备忘录或者邮件里,应用自己得先把内容递给系统分享面板。面板是系统现成的,应用要做的只是把要分享的东西交出去。在鸿蒙应用里做这件事,可以接一个现成的分享库。
这次选了 share_plus,做了一个叫 ShareDeck 的小 Demo。功能很集中:左边是输入区,写好分享文本和标题,点"分享文本"大按钮就拉起系统分享面板;右边一组预设内容的快捷按钮,链接、多行周报、现生成的 txt 文件,点了直接分享;底部一张结果卡片,记录每次调用返回的 ShareResult。活动分享、邀请好友、内容转发这类功能,都可以照这个方式接。
ShareDeck 的完整源码已经放到 AtomGit,文章里的分享台和测试代码都在这里:
项目仓库: sharedeck - AtomGit
分享功能用的是 AtomGit 上 CPF-Flutter 维护的鸿蒙版本(flutter_plus_plugins 聚合仓库里的 share_plus 包),仓库链接统一放在文末。接入代码不多,这次花时间核对的有两块:一是主包和平台接口包要指到同一个提交;二是这个库的鸿蒙实现走的是华为系统分享 kit,编译 SDK 的选择和别的插件不太一样,第一次构建就栽在这里。下面把实际跑通的配置和过程记下来。
环境和设备
还没搭好 Flutter 鸿蒙环境的话,可以先看《Flutter OHOS 开发环境搭建指南》,链接见文末。
我这边用的环境如下,测试日期9月12号。
| 项目 | 版本 |
|---|---|
| Flutter | 3.35.8-ohos-1.0.3,channel [user-branch] |
| Dart | 3.9.2 |
| DevEco Studio | 26.0.0(本机 E:\DevEco Studio) |
| 编译 SDK | OpenHarmony SDK 26.0.0;另用到 DevEco Studio 自带的 HarmonyOS SDK 26.0.0(本机 E:\DevEco Studio\sdk,share_plus 编译必需,原因见 FAQ) |
| 验证方式 | Pura 90 Pro 7.0.0(26.0.0) |
先把依赖选对
这次用的是 share_plus 10.1.1,固定到提交 55de300a8627c55cd45ac86e6a26bcae8e0ca4cf(分支 br_share_plus-v10.1.1_ohos)。它在聚合仓库里的路径是 packages/share_plus/share_plus。下面的配置可以直接放进应用的 pubspec.yaml:
dependencies:
flutter:
sdk: flutter
share_plus:
git:
url: https://atomgit.com/CPF-Flutter/flutter_plus_plugins.git
ref: 55de300a8627c55cd45ac86e6a26bcae8e0ca4cf
path: packages/share_plus/share_plus
dependency_overrides:
share_plus_platform_interface:
git:
url: https://atomgit.com/CPF-Flutter/flutter_plus_plugins.git
ref: 55de300a8627c55cd45ac86e6a26bcae8e0ca4cf
path: packages/share_plus/share_plus_platform_interface
执行:
flutter pub get
share_plus 在两个地方容易配错。
一处是 path。flutter_plus_plugins 是装了一大堆 *_plus 插件的聚合仓库,不能只写仓库地址,share_plus 要落到 packages/share_plus/share_plus 这个子目录。
另一处是 dependency_overrides。只加主包不加覆盖时,Pub 会去 pub.dev 拉 share_plus_platform_interface,和鸿蒙版主包可能不配套。把平台接口包也指到同一个提交,flutter pub get 之后在 pubspec.lock 里能看到两个包的 resolved-ref 都是 55de300…,主包 10.1.1、平台接口 5.0.1,来源一致。以后升级依赖时,这两个包要一起检查。
接口一共三个,外加一个返回值
share_plus 对外就 Share 这一个类,三个静态方法:share、shareUri、shareXFiles。没有要订阅的流,也没有可开关的状态,每次调用就是把内容递出去,等系统分享面板处理。
鸿蒙端底层调的是华为系统分享 kit @hms.collaboration.systemShare:把内容组进一个 SharedData,再交给 ShareController.show() 弹出系统分享面板。Dart 端先导入:
import 'package:share_plus/share_plus.dart';
接口一:分享文本,调用 share:
// text 是正文;subject 是选填的标题,建议始终传
final ShareResult result = await Share.share(text, subject: title);
返回 Future<ShareResult>。subject 在邮件类分享目标里会变成主题;鸿蒙端的实现里它被直接拿来当分享记录的标题(源码里就是 title: subject!),传空有风险,后文 FAQ 单说。
接口二:分享链接,调用 shareUri:
final Uri uri = Uri.parse('https://atomgit.com/CPF-Flutter/flutter_plus_plugins');
final ShareResult result = await Share.shareUri(uri);
参数是一个 Uri 对象,不是字符串。链接类内容各平台会自己做解析,比如 iOS 会抓网页图标放进分享面板。鸿蒙端目前把这个链接转成字符串、按纯文本放进分享记录,所以观感和接口一接近,但语义上还是建议链接走接口二,将来端上实现升级不用改调用方。
接口三:分享文件,调用 shareXFiles:
// 从内存直接造一个 txt,插件会先落到临时目录再分享
final XFile file = XFile.fromData(
utf8.encode(content),
name: 'sharedeck-notes.txt',
mimeType: 'text/plain',
);
final ShareResult result = await Share.shareXFiles(
[file],
subject: 'ShareDeck 笔记',
text: '一份顺手生成的 txt 笔记',
);
文件列表是 List<XFile>,可以一次塞多个;可选参数 text 会作为附带的文字一起分享。鸿蒙端的处理要多一步:每次分享前把 cacheDir/share_plus 这个缓存目录清空,把要分享的文件拷进去,再用 fileUri.getUriFromPath 转成 uri 挂到分享记录上,接收方拿到的是拷贝,不是原文件。
返回值怎么读。 ShareResult 有两个字段:raw 是平台返回的原始字符串,status 是三个取值的枚举 ShareResultStatus:
| 枚举 | 含义 |
|---|---|
ShareResultStatus.success | 用户选了某个分享动作 |
ShareResultStatus.dismissed | 用户直接关掉了分享面板 |
ShareResultStatus.unavailable | 当前平台拿不到分享结果 |
有一点要先说清楚,免得写业务时踩坑:这个版本的鸿蒙端目前拿不到真实的分享结果。翻源码能看到,Dart 侧发出的方法名是 share/shareFiles,鸿蒙侧只在方法名带 WithResult 时才监听面板关闭事件,两边名字对不上,于是调用立即返回 ShareResult.unavailable。也就是说在鸿蒙上,结果回调暂时只能当"面板已拉起"处理,不要拿 status 做业务分支。这条我对照源码确认过,面板上的真实表现还需在真机补实测。
接到分享台上
ShareDeck 就一个主页:左侧输入区一张卡片,装着分享文本输入框、标题输入框和一个"分享文本"大按钮;右侧一张预设内容卡片,链接输入框加三个快捷按钮;窄屏时两卡上下排,宽屏时左右排,用的是 LayoutBuilder 按宽度切布局。底部常驻一张结果卡片,显示最近一次调用的接口名、status 和 raw。
所有分享按钮最后都汇到同一个方法里,调接口、等返回、把结果写进卡片,错误也在这里兜住:
Future<void> _runShare(
String action,
Future<ShareResult> Function() call,
) async {
try {
final ShareResult result = await call();
if (!mounted) return;
setState(() {
_lastAction = action;
_lastDetail = 'status = ${result.status.name},raw = "${result.raw}"';
_hasResult = true;
});
} on PlatformException catch (e) {
// 系统分享服务没起来、参数被拒这类错误都会走到这
if (!mounted) return;
setState(() {
_lastAction = action;
_lastDetail = '调用失败:code=${e.code},message=${e.message}';
_hasResult = true;
});
}
}
四个按钮各自只是往里传不同的闭包,比如大按钮这边,文本为空时直接拦下弹提示,不发起调用:
Future<void> _shareText() async {
final String text = _textCtrl.text.trim();
if (text.isEmpty) {
_toast('先写点内容,空的没法分享');
return;
}
await _runShare(
'Share.share(文本)',
() => Share.share(text, subject: _subjectOr('ShareDeck 分享')),
);
}
_subjectOr 是个兜底:标题输入框留空时传默认标题。因为鸿蒙端拿 subject 直接当分享记录标题,Demo 里不给空值机会。
这是分享台的初始样子,宽屏下输入区和快捷区左右两栏,底部结果卡片显示"还没分享过":

点"分享文本"后,系统分享面板从底部弹起,输入的正文和标题都出现在面板里,选一个分享目标就能发出去:

关键文件就一个:lib/main.dart,页面布局、输入校验、调用和结果展示全在这一个文件里。share_plus 不需要订阅状态流,也就没有 dispose 里取消订阅的负担,控制器照常释放即可。
下载源码,签名后装到手机
可以直接把 ShareDeck 拉下来运行,依赖已经写进工程:
git clone https://atomgit.com/gcw_uKGkQkkd/sharedeck.git
cd sharedeck
flutter pub get
注意两点。一是 Windows 命令行构建前要设 DEVECO_SDK_HOME 环境变量,指向 DevEco Studio 的 SDK 目录(IDE 里构建则不用管,DevEco 会自己带):
export DEVECO_SDK_HOME='E:\DevEco Studio\sdk'
二是在 DevEco Studio 里完成调试签名:
-
用 DevEco Studio 打开
ohos目录。 -
进入 File → Project Structure → Signing Configs。
-
勾选 Automatically generate signature,给当前工程和设备生成签名,点 Apply/OK。
签名完成后构建、安装:
flutter build hap --release
hdc list targets
hdc -t <设备ID> install -r build/ohos/hap/entry-default-signed.hap
hdc -t <设备ID> shell aa start -a EntryAbility -b com.example.sharedeck
平时调页面需要热重载,可用 flutter run -d <设备ID>。
手机上实际试了什么
本机静态检查与测试结果:
-
flutter analyze:通过,无问题。 -
flutter test:冒烟测试通过(两条用例:验证分享台页面正常创建、三个接口按钮都渲染到位;以及清空文本点大按钮只弹提示、不发起调用)。 -
DEVECO_SDK_HOME='E:\DevEco Studio\sdk'下执行flutter build hap --debug --no-codesign:构建成功,产物build/ohos/hap/entry-default-unsigned.hap。
模拟器上逐项操作后的结果如下:
| 操作 | 预期结果 |
|---|---|
| 进页面 | 双栏布局,输入框有默认文案,底部显示"还没分享过" |
| 清空文本点"分享文本" | 只弹 SnackBar 提示,不拉起分享面板 |
| 写好文本点"分享文本" | 拉起系统分享面板,标题和正文与输入一致 |
| 点"分享这条链接" | 面板出现链接内容,可选择备忘录等目标 |
| 点"分享多行周报" | 多行文本在面板预览中保留换行 |
| 点"生成 txt 并分享" | 面板出现 sharedeck-notes.txt 文件卡片 |
| 面板里选目标或直接关闭 | 底部结果卡片显示 status = unavailable(鸿蒙端当前行为,见接口一节) |

FAQ:接入时遇到的问题
CompileArkTS 报 Cannot find module '@hms.collaboration.systemShare'
我第一次构建就报了这个:Execution failed for task 'default@CompileArkTS',日志里还有一句 "@hms.collaboration.systemShare" ... could not be resolved。原因是 share_plus 的鸿蒙实现用的是华为系统分享 kit,@hms. 开头的模块在纯 OpenHarmony SDK(比如 C:\OHSDK)里根本不存在,连带的还有两个 arkts-no-any-unknown 报错,其实都是同一个根因。解决办法是把工程 ohos/build-profile.json5 里 product 的 runtimeOS 从 OpenHarmony 改成 HarmonyOS,再让构建进程能找到带 hms 组件的 SDK。命令行构建要设环境变量 DEVECO_SDK_HOME 指向 DevEco Studio 自带 SDK(本机是 E:\DevEco Studio\sdk,里面 default/openharmony 和 default/hms 成对出现)。注意 local.properties 里写 hwsdk.dir 是没用的,hvigor 会把这个键丢掉,只认 DEVECO_SDK_HOME 环境变量。改完重新构建就过了。
平台接口包从 pub.dev 解析,版本对不上
判断办法:flutter pub get 之后打开 pubspec.lock,搜 share_plus_platform_interface,看它的 source 是不是 git、resolved-ref 是否和主包一致。如果 source: hosted,说明 dependency_overrides 没配或 path 写错,指到聚合仓库的 packages/share_plus/share_plus_platform_interface、和主包同一个提交即可。改完要重新 pub get;涉及原生依赖的变化要重建 HAP,热重载不生效。
分享结果 status 一直是 unavailable
先看接口一节的源码分析:Dart 侧发的方法名和鸿蒙侧监听关闭事件的条件对不上,面板关闭后不会把结果回传,调用即刻返回 unavailable。这不是接错了,是这个版本的实现现状。业务上在鸿蒙端把分享当"发出即完成"处理就好,别写依赖 success/dismissed 的逻辑。如果后续版本修了这里,pubspec.lock 里的提交号会变,升级时重新验证一遍即可。
subject 传空会怎样
没实测面板表现,但源码里鸿蒙端构造分享记录写的是 title: subject!,subject 的空值被直接塞给了标题字段,行为没有兜底。稳妥的做法是像 Demo 里那样自己兜底:输入框留空就传一个默认标题,永远不给端上递空值。
Git Bash 里构建报 BATCH RECURSION / ohpm install failed
在 Git Bash 里运行 bin/flutter 会把 OS 环境变量改写成 MINGW64_NT-...,DevEco 的 ohpm.bat 依赖 %OS%=="Windows_NT" 的判断,判断失败后内部一段批处理递归不收敛,直接爆栈。绕开的办法是直接调批处理入口:
PUB_CACHE='E:\OhProject\.pub-cache' /e/FLOH/flutter_flutter/bin/flutter.bat build hap --debug --no-codesign
用 PowerShell 或在 DevEco Studio 里构建则不受影响。
发现库的问题,怎么反馈
1. 提交 Issue,说明问题和复现步骤
从文末链接进入 flutter_plus_plugins 仓库,打开 Issues 页面,先搜索有没有相同的问题。如果没有,点击右上角的 新建 Issue,填写问题标题和复现信息。

报告时把 pubspec.yaml 的依赖配置和 pubspec.lock 里实际解析的提交号贴出来,说明预期什么、实际报什么错,以及 Flutter 版本、设备系统。像"分享结果回传"这种问题,最好附上面板操作步骤和日志。
2. Fork 项目,在自己的仓库中修改
如果问题能自己修,点击上游仓库右上角的 Fork。在 Fork 页面确认项目名称、自己的账号和要复制的分支,再点击 创建 Fork 项目。

Fork 完成后,把自己的仓库克隆到本地,并添加上游仓库。下面以给 share_plus 的中文说明补一条 SDK 要求为例,<你的账号> 换成自己的 AtomGit 账号:
git clone https://atomgit.com/<你的账号>/flutter_plus_plugins.git
cd flutter_plus_plugins
git remote add upstream https://atomgit.com/CPF-Flutter/flutter_plus_plugins.git
git fetch upstream
git switch -c docs/ohos-share-plus-sdk-note upstream/br_share_plus-v10.1.1_ohos
改好后检查差异并推到自己的 Fork:
git diff --check
git add [想提交的文件]
git commit -m "docs: note HarmonyOS SDK requirement for share_plus ohos"
git push -u origin docs/ohos-share-plus-sdk-note
3. 创建 PR,把修改提交给上游
推送后回到自己的 AtomGit Fork 仓库,点击 同步源项目,选择包含修改的分支。有超前提交时,可以通过弹窗中的 创建并提交 PR 进入 PR 创建页面。

在 PR 页面核对源仓库和源分支,目标仓库选 CPF-Flutter/flutter_plus_plugins 上游,目标分支选 br_share_plus-v10.1.1_ohos。说明里写清原来哪里不对、改了什么、用哪个版本验证过;有对应 Issue 就关联上,确认差异后提交 PR。
相关链接
更多推荐




所有评论(0)