通过网盘分享的文件:flutter1.zip
链接: https://pan.baidu.com/s/1jkLZ9mZXjNm0LgP6FTVRzw 提取码: 2t97
分享是社交传播的重要途径,用户看到喜欢的动漫想分享给朋友。在 HarmonyOS 上,分享需要调用原生的 ShareKit,Flutter 通过 MethodChannel 与原生代码通信。
这篇文章会实现分享功能,讲解 MethodChannel 平台通道的使用、异常处理,以及如何设计一个健壮的分享服务。

分享功能的实现思路
Flutter 本身没有分享能力,需要调用原生平台的分享功能。在 HarmonyOS 上是 ShareKit,在 Android 上是 Intent,在 iOS 上是 UIActivityViewController。
MethodChannel 是 Flutter 和原生代码通信的桥梁,Flutter 发送消息,原生代码接收并处理。
MethodChannel 基础
dart
import 'package:flutter/services.dart';
class ShareService {
static const MethodChannel _channel = MethodChannel('com.example.flutter1/share');
MethodChannel 需要一个唯一的名称,通常用包名 + 功能名的格式。
这个名称在 Flutter 和原生代码两边必须一致,否则无法通信。
分享文本方法
dart
static Future<bool> shareText({
required String title,
required String content,
}) async {
try {
print('🔄 ShareService: 调用原生分享...');
await _channel.invokeMethod('shareText', {
'title': title,
'content': content,
});
print('✅ ShareService: 分享面板已弹出');
return true;
} on MissingPluginException catch (e) {
print('❌ ShareService: 插件未注册: $e');
return false;
} on PlatformException catch (e) {
print('❌ ShareService: 平台错误: ${e.message}');
return false;
} catch (e) {
print('❌ ShareService: 未知错误: $e');
return false;
}
}
invokeMethod 调用原生方法,第一个参数是方法名,第二个参数是传递的数据。
返回 Future,因为原生调用是异步的。
异常处理详解
dart
on MissingPluginException catch (e) {
print('❌ ShareService: 插件未注册: $e');
return false;
}
MissingPluginException 表示原生端没有注册这个 Channel。可能是原生代码没写,或者 Channel 名称不一致。
dart
on PlatformException catch (e) {
print('❌ ShareService: 平台错误: ${e.message}');
return false;
}
PlatformException 表示原生代码执行出错,比如权限不足、参数错误等。
dart
catch (e) {
print('❌ ShareService: 未知错误: $e');
return false;
}
兜底的异常处理,捕获其他未知错误。
分享链接方法
dart
static Future<bool> shareUrl({
required String title,
required String url,
String? description,
}) async {
try {
print('🔄 ShareService: 调用原生分享链接...');
await _channel.invokeMethod('shareUrl', {
'title': title,
'url': url,
'description': description ?? '',
});
print('✅ ShareService: 分享面板已弹出');
return true;
} on MissingPluginException catch (e) {
print('❌ ShareService: 插件未注册: $e');
return false;
} on PlatformException catch (e) {
print('❌ ShareService: 平台错误: ${e.message}');
return false;
} catch (e) {
print('❌ ShareService: 未知错误: $e');
return false;
}
}
分享链接和分享文本类似,多了一个 url 参数。
description 是可选的描述,用 ?? '' 处理 null 值。
在详情页使用分享
dart
import '../services/share_service.dart';
// 在 AppBar 的 actions 里添加分享按钮
IconButton(
icon: const Icon(Icons.share),
onPressed: () async {
final success = await ShareService.shareText(
title: _anime.title,
content: '${_anime.synopsis ?? '暂无简介'}\n\n来自微动漫App',
);
if (!success && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('分享失败,请稍后重试')),
);
}
},
),
点击分享按钮调用 ShareService,失败时显示提示。
mounted 检查组件是否还在树中,避免异步操作后组件已销毁的问题。
分享内容的格式化
分享内容要有吸引力:
dart
String _formatShareContent(Anime anime) {
final buffer = StringBuffer();
buffer.writeln('【${anime.title}】');
buffer.writeln();
if (anime.score != null) {
buffer.writeln('⭐ 评分: ${anime.score}');
}
if (anime.episodes != null) {
buffer.writeln('📺 集数: ${anime.episodes}集');
}
if (anime.synopsis != null) {
final synopsis = anime.synopsis!.length > 100
? '${anime.synopsis!.substring(0, 100)}...'
: anime.synopsis!;
buffer.writeln();
buffer.writeln(synopsis);
}
buffer.writeln();
buffer.writeln('来自微动漫App');
return buffer.toString();
}
StringBuffer 高效拼接字符串。
简介太长时截断,加省略号。
最后加上 App 名称,做品牌露出。
分享弹窗
可以先弹窗让用户选择分享方式:
dart
void _showShareDialog(BuildContext context, Anime anime) {
showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.text_fields),
title: const Text('分享文本'),
onTap: () {
Navigator.pop(context);
_shareAsText(anime);
},
),
ListTile(
leading: const Icon(Icons.link),
title: const Text('分享链接'),
onTap: () {
Navigator.pop(context);
_shareAsLink(anime);
},
),
ListTile(
leading: const Icon(Icons.copy),
title: const Text('复制链接'),
onTap: () {
Navigator.pop(context);
_copyLink(anime);
},
),
],
),
);
}
showModalBottomSheet 弹出底部菜单,提供多种分享方式。
复制到剪贴板
dart
import 'package:flutter/services.dart';
void _copyLink(Anime anime) {
final url = 'https://myanimelist.net/anime/${anime.malId}';
Clipboard.setData(ClipboardData(text: url));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('链接已复制')),
);
}
Clipboard.setData 复制文本到剪贴板。
复制成功后用 SnackBar 提示用户。
分享图片
如果要分享图片,需要先下载到本地:
dart
static Future<bool> shareImage({
required String imagePath,
String? title,
}) async {
try {
await _channel.invokeMethod('shareImage', {
'imagePath': imagePath,
'title': title ?? '',
});
return true;
} catch (e) {
print('❌ ShareService: 分享图片失败: $e');
return false;
}
}
imagePath 是本地图片路径,不是网络 URL。
需要先用 http 包下载图片,保存到临时目录,再分享。
下载图片再分享
dart
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
Future<void> _shareWithImage(Anime anime) async {
if (anime.imageUrl == null) {
_shareAsText(anime);
return;
}
try {
// 下载图片
final response = await http.get(Uri.parse(anime.imageUrl!));
// 保存到临时目录
final tempDir = await getTemporaryDirectory();
final file = File('${tempDir.path}/share_image.jpg');
await file.writeAsBytes(response.bodyBytes);
// 分享
await ShareService.shareImage(
imagePath: file.path,
title: anime.title,
);
} catch (e) {
print('❌ 分享图片失败: $e');
_shareAsText(anime); // 降级为文本分享
}
}
下载失败时降级为文本分享,保证功能可用。
分享结果回调
有些平台支持分享结果回调:
dart
static Future<ShareResult> shareTextWithResult({
required String title,
required String content,
}) async {
try {
final result = await _channel.invokeMethod<String>('shareTextWithResult', {
'title': title,
'content': content,
});
switch (result) {
case 'success':
return ShareResult.success;
case 'cancelled':
return ShareResult.cancelled;
default:
return ShareResult.unknown;
}
} catch (e) {
return ShareResult.failed;
}
}
enum ShareResult {
success,
cancelled,
failed,
unknown,
}
invokeMethod 指定返回类型,原生代码返回分享结果。
根据结果可以做不同的处理,比如分享成功后显示感谢提示。
分享按钮组件
可以封装一个分享按钮组件:
dart
class ShareButton extends StatelessWidget {
final String title;
final String content;
final String? url;
final VoidCallback? onShareComplete;
const ShareButton({
super.key,
required this.title,
required this.content,
this.url,
this.onShareComplete,
});
@override
Widget build(BuildContext context) {
return IconButton(
icon: const Icon(Icons.share),
onPressed: () async {
final success = url != null
? await ShareService.shareUrl(title: title, url: url!)
: await ShareService.shareText(title: title, content: content);
if (success) {
onShareComplete?.call();
} else {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('分享失败')),
);
}
}
},
);
}
}
封装后使用更简单:
dart
ShareButton(
title: anime.title,
content: anime.synopsis ?? '',
onShareComplete: () {
print('分享完成');
},
)
分享统计
可以记录分享次数:
dart
static Future<bool> shareText({
required String title,
required String content,
}) async {
try {
await _channel.invokeMethod('shareText', {
'title': title,
'content': content,
});
// 记录分享统计
_recordShare('text', title);
return true;
} catch (e) {
return false;
}
}
static void _recordShare(String type, String title) {
// 发送到统计服务
print('📊 分享统计: type=$type, title=$title');
}
分享统计可以帮助了解用户行为,优化产品。
小结
分享功能涉及的技术点:MethodChannel 平台通道 、invokeMethod 调用原生方法 、异常处理 、Clipboard 剪贴板 、showModalBottomSheet 底部菜单。
MethodChannel 是 Flutter 和原生代码通信的桥梁,Channel 名称两边必须一致。
异常处理很重要:MissingPluginException 表示插件未注册,PlatformException 表示原生执行出错。
好的分享功能要提供多种方式(文本、链接、图片),失败时要有降级方案和用户提示。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net