欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

本文基于flutter3.27.5开发

一、share_plus 库概述

分享功能是移动应用中常见的社交特性,用户可以将文本、链接、图片、文件等内容分享到其他应用。在 Flutter for OpenHarmony 应用开发中,share_plus 是一个功能强大的分享插件,提供了完整的跨平台分享能力。

share_plus 库特点

share_plus 库基于 Flutter 平台接口实现,提供了以下核心特性:

多类型分享:支持分享文本、URL、图片、文件等多种类型的内容,满足各种分享场景需求。

系统分享面板:调用系统原生分享面板,用户可以选择任意支持的应用进行分享,体验一致流畅。

分享结果反馈:提供分享结果回调,可以获知用户是完成了分享还是取消了操作。

多文件分享:支持同时分享多个文件,并提供 MIME 类型设置。

功能支持对比

功能 Android iOS OpenHarmony
分享文本
分享 URL
分享图片
分享文件
多文件分享
分享结果
分享位置

使用场景:分享文本内容、分享网页链接、分享图片到社交应用、分享文件到其他应用等。


二、安装与配置

2.1 添加依赖(完整代码中使用到了其他库,具体请看完整代码部分)

在项目的 pubspec.yaml 文件中添加 share_plus 依赖:

dependencies:
  share_plus:
    git:
      url: https://atomgit.com/openharmony-sig/flutter_plus_plugins.git
      path: packages/share_plus/share_plus

然后执行以下命令获取依赖:

flutter pub get

2.2 兼容性信息

项目 版本要求
Flutter SDK 3.7.12-ohos-1.0.6
OpenHarmony SDK 5.0.0 (API 12)
DevEco Studio 5.0.13.200
ROM 5.1.0.120 SP3

2.3 权限配置

share_plus 在 OpenHarmony 平台上不需要额外权限配置,系统分享面板会自动处理相关权限。


三、核心 API 详解

3.1 Share.share 方法

Share.share 是最基础的分享方法,用于分享文本内容。

static Future<void> share(
  String text, {
  String? subject,
  Rect? sharePositionOrigin,
})

参数说明

text 参数是要分享的文本内容,不能为空。

subject 参数是可选的主题,当用户选择邮件分享时会作为邮件主题使用。

sharePositionOrigin 参数用于指定分享面板的弹出位置,在 iPad 和 Mac 上有效。

使用示例

await Share.share('这是一段要分享的文本内容');

3.2 Share.shareUri 方法

Share.shareUri 用于分享 URL 链接。

static Future<void> shareUri(Uri uri)

参数说明

uri 参数是要分享的 URI 对象。

使用示例

await Share.shareUri(Uri.parse('https://flutter.dev'));

在 iOS 上,系统会自动获取网页的图标和预览信息显示在分享面板中。

3.3 Share.shareXFiles 方法

Share.shareXFiles 用于分享一个或多个文件。

static Future<ShareResult> shareXFiles(
  List<XFile> files, {
  String? subject,
  String? text,
  Rect? sharePositionOrigin,
})

参数说明

files 参数是要分享的文件列表,使用 XFile 对象表示。

subject 参数是可选的主题。

text 参数是可选的附加文本。

sharePositionOrigin 参数用于指定分享面板的弹出位置。

返回值:返回 ShareResult 对象,包含分享结果状态。

使用示例

final List<XFile> files = [
  XFile('/path/to/image.jpg', name: 'image.jpg', mimeType: 'image/jpeg'),
];
await Share.shareXFiles(files, text: '分享图片');

3.4 Share.shareWithResult 方法

Share.shareWithResult 用于分享文本并获取分享结果。

static Future<ShareResult> shareWithResult(
  String text, {
  String? subject,
  Rect? sharePositionOrigin,
})

返回值:返回 ShareResult 对象,包含分享结果状态。

使用示例

final result = await Share.shareWithResult('分享内容');
if (result.status == ShareResultStatus.success) {
  print('分享成功');
} else if (result.status == ShareResultStatus.dismissed) {
  print('用户取消分享');
}

3.5 ShareResult 类

ShareResult 类包含分享操作的结果信息。

class ShareResult {
  final String raw;
  final ShareResultStatus status;
}

属性说明

raw 属性是平台返回的原始结果,空字符串表示用户取消分享。

status 属性是分享结果状态枚举。

3.6 ShareResultStatus 枚举

ShareResultStatus 枚举定义了分享结果的状态。

ShareResultStatus.success 表示用户选择了某个分享动作并完成分享。

ShareResultStatus.dismissed 表示用户取消了分享操作。

ShareResultStatus.unavailable 表示状态无法确定,通常表示平台不支持返回分享结果。

3.7 XFile 类

XFile 类用于表示要分享的文件。

XFile(
  String path, {
  String? name,
  String? mimeType,
})

参数说明

path 参数是文件的路径。

name 参数是文件名,可选。

mimeType 参数是文件的 MIME 类型,可选。

静态方法

XFile.fromData(Uint8List bytes, {String? name, String? mimeType}) 从字节数据创建文件。


四、实战案例

4.1 分享文本

Future<void> shareText() async {
  await Share.share(
    '这是一段要分享的文本内容,可以包含任意文字。',
    subject: '分享主题',
  );
}

4.2 分享 URL

Future<void> shareUrl() async {
  final Uri url = Uri.parse('https://flutter.dev');
  await Share.shareUri(url);
}

4.3 分享图片

Future<void> shareImage(String imagePath) async {
  final XFile file = XFile(
    imagePath,
    name: 'shared_image.jpg',
    mimeType: 'image/jpeg',
  );
  await Share.shareXFiles([file], text: '分享一张图片');
}

4.4 分享多个文件

Future<void> shareMultipleFiles(List<String> paths) async {
  final List<XFile> files = paths.map((path) {
    return XFile(path, mimeType: 'image/jpeg');
  }).toList();
  
  await Share.shareXFiles(
    files,
    subject: '分享多张图片',
    text: '这是多张图片的分享',
  );
}

4.5 分享并获取结果

Future<void> shareWithResult() async {
  final ShareResult result = await Share.shareWithResult(
    '分享内容',
    subject: '分享主题',
  );
  
  switch (result.status) {
    case ShareResultStatus.success:
      print('分享成功: ${result.raw}');
      break;
    case ShareResultStatus.dismissed:
      print('用户取消分享');
      break;
    case ShareResultStatus.unavailable:
      print('分享结果不可用');
      break;
  }
}

4.6 从资源文件分享

Future<void> shareFromAssets() async {
  final ByteData bytes = await rootBundle.load('assets/image.png');
  final Uint8List list = bytes.buffer.asUint8List();
  
  final XFile file = XFile.fromData(
    list,
    name: 'shared_image.png',
    mimeType: 'image/png',
  );
  
  await Share.shareXFiles([file]);
}

五、最佳实践

5.1 指定分享面板位置

在 iPad 和 Mac 上,需要指定分享面板的弹出位置:

final box = context.findRenderObject() as RenderBox?;
if (box != null) {
  await Share.share(
    '分享内容',
    sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size,
  );
}

5.2 错误处理

对分享操作进行错误处理:

Future<void> safeShare(String text) async {
  try {
    await Share.share(text);
  } on PlatformException catch (e) {
    print('分享失败: ${e.message}');
  } catch (e) {
    print('发生错误: $e');
  }
}

5.3 检查内容有效性

分享前检查内容是否有效:

Future<void> shareIfValid(String? text) async {
  if (text == null || text.isEmpty) {
    print('分享内容为空');
    return;
  }
  await Share.share(text);
}

5.4 结合图片选择器

结合 image_picker 实现选择图片后分享:

Future<void> pickAndShare() async {
  final ImagePicker picker = ImagePicker();
  final XFile? image = await picker.pickImage(source: ImageSource.gallery);
  
  if (image != null) {
    await Share.shareXFiles([image], text: '分享选中的图片');
  }
}

六、常见问题

Q1:如何分享带格式的文本?

share_plus 只支持纯文本分享,不支持富文本格式。如需分享带格式内容,可以考虑生成图片或 PDF 后分享。

Q2:如何知道用户分享到了哪个应用?

出于隐私考虑,平台不会告知用户选择了哪个应用进行分享。ShareResult 只能知道用户是否完成了分享操作。

Q3:分享大文件时需要注意什么?

分享大文件时建议先检查文件是否存在,并提示用户等待:

final file = File(path);
if (await file.exists()) {
  await Share.shareXFiles([XFile(path)]);
}

Q4:如何在 OpenHarmony 上调试分享功能?

在 OpenHarmony 上,分享功能会调用系统分享面板。确保设备上安装了可接收分享内容的应用。

Q5:分享图片后图片存储在哪里?

分享的图片会被复制到系统临时目录,接收方应用会处理图片的存储。


七、总结

share_plus 库为 Flutter for OpenHarmony 开发提供了完整的分享功能。通过简洁的 API,开发者可以轻松实现文本、URL、图片、文件的分享。该库在鸿蒙平台上已经完成了完整的适配,支持所有核心功能,开发者可以放心使用。


八、完整代码示例

在这里插入图片描述

以下是一个完整的可运行示例,展示了 share_plus 库的核心功能:

pubspec.yaml

name: share_plus_demo
description: Flutter for OpenHarmony share_plus 演示项目
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  share_plus:
    git:
      url: https://atomgit.com/openharmony-sig/flutter_plus_plugins.git
      path: packages/share_plus/share_plus
  image_picker:
    git:
      url: https://atomgit.com/openharmony-tpc/flutter_packages.git
      path: packages/image_picker/image_picker

flutter:
  uses-material-design: true
  assets:
    - assets/

main.dart

import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Share Plus Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final TextEditingController _textController = TextEditingController();
  final TextEditingController _subjectController = TextEditingController();
  final TextEditingController _urlController = TextEditingController();
  final List<XFile> _selectedFiles = [];
  String _resultMessage = '';

  Future<void> _shareText() async {
    if (_textController.text.isEmpty) {
      _showMessage('请输入要分享的文本');
      return;
    }
  
    try {
      await Share.share(
        _textController.text,
        subject: _subjectController.text.isEmpty ? null : _subjectController.text,
      );
      _showMessage('分享完成');
    } catch (e) {
      _showMessage('分享失败: $e');
    }
  }

  Future<void> _shareUrl() async {
    if (_urlController.text.isEmpty) {
      _showMessage('请输入要分享的 URL');
      return;
    }
  
    try {
      final Uri uri = Uri.parse(_urlController.text);
      await Share.shareUri(uri);
      _showMessage('分享完成');
    } on FormatException catch (e) {
      _showMessage('URL 格式错误: $e');
    } catch (e) {
      _showMessage('分享失败: $e');
    }
  }

  Future<void> _shareWithResult() async {
    if (_textController.text.isEmpty) {
      _showMessage('请输入要分享的文本');
      return;
    }
  
    try {
      final result = await Share.shareWithResult(
        _textController.text,
        subject: _subjectController.text.isEmpty ? null : _subjectController.text,
      );
    
      switch (result.status) {
        case ShareResultStatus.success:
          _showMessage('分享成功');
          break;
        case ShareResultStatus.dismissed:
          _showMessage('用户取消分享');
          break;
        case ShareResultStatus.unavailable:
          _showMessage('分享结果不可用');
          break;
      }
    } catch (e) {
      _showMessage('分享失败: $e');
    }
  }

  Future<void> _pickImage() async {
    final ImagePicker picker = ImagePicker();
    final XFile? image = await picker.pickImage(source: ImageSource.gallery);
  
    if (image != null) {
      setState(() {
        _selectedFiles.add(image);
      });
    }
  }

  Future<void> _pickMultipleImages() async {
    final ImagePicker picker = ImagePicker();
    final List<XFile> images = await picker.pickMultiImage();
  
    if (images.isNotEmpty) {
      setState(() {
        _selectedFiles.addAll(images);
      });
    }
  }

  void _removeFile(int index) {
    setState(() {
      _selectedFiles.removeAt(index);
    });
  }

  Future<void> _shareFiles() async {
    if (_selectedFiles.isEmpty) {
      _showMessage('请先选择要分享的文件');
      return;
    }
  
    try {
      await Share.shareXFiles(
        _selectedFiles,
        text: _textController.text.isEmpty ? null : _textController.text,
        subject: _subjectController.text.isEmpty ? null : _subjectController.text,
      );
      _showMessage('分享完成');
    } catch (e) {
      _showMessage('分享失败: $e');
    }
  }

  Future<void> _shareFromAssets() async {
    try {
      final ByteData bytes = await rootBundle.load('assets/flutter_logo.png');
      final Uint8List list = bytes.buffer.asUint8List();
    
      final XFile file = XFile.fromData(
        list,
        name: 'flutter_logo.png',
        mimeType: 'image/png',
      );
    
      await Share.shareXFiles([file], text: '分享 Flutter Logo');
      _showMessage('分享完成');
    } catch (e) {
      _showMessage('分享失败: $e');
    }
  }

  void _showMessage(String message) {
    setState(() {
      _resultMessage = message;
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  
  void dispose() {
    _textController.dispose();
    _subjectController.dispose();
    _urlController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Share Plus 演示'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const Text(
              'Share Plus 功能演示',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 24),
          
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('分享文本', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                    const SizedBox(height: 12),
                    TextField(
                      controller: _textController,
                      decoration: const InputDecoration(
                        labelText: '分享内容',
                        border: OutlineInputBorder(),
                      ),
                      maxLines: 3,
                    ),
                    const SizedBox(height: 12),
                    TextField(
                      controller: _subjectController,
                      decoration: const InputDecoration(
                        labelText: '主题(可选)',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _shareText,
                            icon: const Icon(Icons.share),
                            label: const Text('分享文本'),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _shareWithResult,
                            icon: const Icon(Icons.share_rounded),
                            label: const Text('分享并获取结果'),
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
          
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('分享 URL', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                    const SizedBox(height: 12),
                    TextField(
                      controller: _urlController,
                      decoration: const InputDecoration(
                        labelText: 'URL 地址',
                        border: OutlineInputBorder(),
                        hintText: 'https://flutter.dev',
                      ),
                      keyboardType: TextInputType.url,
                    ),
                    const SizedBox(height: 12),
                    ElevatedButton.icon(
                      onPressed: _shareUrl,
                      icon: const Icon(Icons.link),
                      label: const Text('分享 URL'),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
          
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('分享文件', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _pickImage,
                            icon: const Icon(Icons.image),
                            label: const Text('选择图片'),
                          ),
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: ElevatedButton.icon(
                            onPressed: _pickMultipleImages,
                            icon: const Icon(Icons.photo_library),
                            label: const Text('选择多张'),
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    if (_selectedFiles.isNotEmpty) ...[
                      const Text('已选择的文件:', style: TextStyle(fontWeight: FontWeight.w500)),
                      const SizedBox(height: 8),
                      ...List.generate(_selectedFiles.length, (index) {
                        return ListTile(
                          dense: true,
                          leading: const Icon(Icons.insert_drive_file),
                          title: Text(_selectedFiles[index].name),
                          trailing: IconButton(
                            icon: const Icon(Icons.close),
                            onPressed: () => _removeFile(index),
                          ),
                        );
                      }),
                      const SizedBox(height: 12),
                    ],
                    ElevatedButton.icon(
                      onPressed: _shareFiles,
                      icon: const Icon(Icons.attach_file),
                      label: const Text('分享选中的文件'),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
          
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('从资源分享', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
                    const SizedBox(height: 12),
                    ElevatedButton.icon(
                      onPressed: _shareFromAssets,
                      icon: const Icon(Icons.archive),
                      label: const Text('分享资源文件'),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),
          
            if (_resultMessage.isNotEmpty)
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.blue.shade50,
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: Colors.blue.shade200),
                ),
                child: Text(
                  _resultMessage,
                  style: const TextStyle(fontSize: 14),
                ),
              ),
          ],
        ),
      ),
    );
  }
}


九、参考资源

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐