Flutter分享处理插件zikzak_share_handler的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter分享处理插件zikzak_share_handler的使用

zikzak_share_handler 是一个用于Flutter的插件,支持iOS和Android平台,能够处理接收到的共享文本/媒体,并允许将共享内容添加到建议/快捷方式中。本文将详细介绍如何安装和配置该插件,并提供一个完整的示例Demo。

1. 安装

首先,在 pubspec.yaml 文件中添加 zikzak_share_handler 作为依赖:

dependencies:
  zikzak_share_handler: ^latest_version

2. iOS配置

2.1 修改 Info.plist

<project root>/ios/Runner/Info.plist 文件中添加以下内容,以注册应用通过深度链接启动,并请求访问照片库的权限:

<!-- Add for zikzak_share_handler start -->
<!-- 如果你计划使用 recordSentMessage API,使对话显示为直接分享建议,则需要以下键 -->
<key>NSUserActivityTypes</key>
<array>
    <string>INSendMessageIntent</string>
</array>

<!-- 如果你想使用自定义组ID而不是默认值,请取消注释以下行,并在Build Settings -> User-Defined中设置 -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        </array>
    </dict>
</array>

<key>NSPhotoLibraryUsageDescription</key>
<string>照片可以被分享并在此应用中使用</string>

<!-- 可选:添加或自定义以支持AirDrop -->
<key>LSSupportsOpeningDocumentsInPlace</key>
<string>No</string>
<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>ShareHandler</string>
        <key>LSHandlerRank</key>
        <string>Alternate</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.file-url</string>
            <string>public.image</string>
            <string>public.text</string>
            <string>public.movie</string>
            <string>public.url</string>
            <string>public.data</string>
        </array>
    </dict>
</array>

<!-- Add for zikzak_share_handler end -->
2.2 创建Share Extension
  1. 在Xcode中,选择 File -> New -> Target,然后选择 “Share Extension”。
  2. 将其命名为 ShareExtension 并保存。
2.3 修改 ShareExtension/Info.plist

编辑 &lt;project root&gt;/ios/ShareExtension/Info.plist 文件,添加以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- 如果你想使用自定义组ID而不是默认值,请取消注释以下行,并在Build Settings -> User-Defined中设置 -->
    <!-- <key>AppGroupId</key>
    <string>$(CUSTOM_GROUP_ID)</string> -->

    <key>CFBundleVersion</key>
    <string>$(FLUTTER_BUILD_NUMBER)</string>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <!-- 如果你支持分享到特定对话,请添加支持的消息意图 -->
            <key>IntentsSupported</key>
            <array>
                <string>INSendMessageIntent</string>
            </array>
            <!-- 删除仅在开发模式下工作的 TRUEPREDICATE NSExtensionActivationRule -->
            <!-- <string>TRUEPREDICATE</string> -->
            <!-- 添加新的 NSExtensionActivationRule,允许分享一个或多个任意类型的文件、URL 或文本内容 -->
            <string>SUBQUERY (
                extensionItems,
                $extensionItem,
                SUBQUERY (
                    $extensionItem.attachments,
                    $attachment,
                    (
                        ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
                        || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image"
                        || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text"
                        || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie"
                        || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url"
                    )
                ).@count > 0
            ).@count > 0
            </string>
            <key>PHSupportedMediaTypes</key>
            <array>
                <string>Video</string>
                <string>Image</string>
            </array>
        </dict>
        <key>NSExtensionMainStoryboard</key>
        <string>MainInterface</string>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
    </dict>
</dict>
</plist>
2.4 添加App Group
  1. 在Xcode中,选择 Runner -> Targets -> Runner -> Signing & Capabilities
  2. 点击 + 按钮并选择 App Groups
  3. 添加一个新的组(默认是你的bundle identifier前缀为 group.,例如 group.wtf.zikzak)。
  4. ShareExtension 目标重复上述步骤,选择相同的组ID。
2.5 (可选) 自定义组ID

如果你使用了自定义组ID而不是默认的 group.<bundle_identifier>,请确保在 ShareExtensioninfo.plist 文件中引用自定义构建设置变量:

  1. 进入 Targets -> ShareExtension -> Build Settings
  2. 点击 + 图标并选择 Add User-Defined Setting
  3. 给它设置键 CUSTOM_GROUP_ID,值为你给两个目标的组ID。
  4. Runner 目标重复上述步骤。
2.6 修改 Podfile

&lt;project root&gt;/ios/Podfile 文件中,在 target 'Runner' do 块内添加以下代码,然后运行 pod install

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

  # zikzak_share_handler addition start
  target 'ShareExtension' do
    inherit! :search_paths
    pod "zikzak_share_handler_ios_models", :path => ".symlinks/plugins/zikzak_share_handler_ios/ios/Models"
  end
  # zikzak_share_handler addition end
end
2.7 替换 ShareViewController.swift

&lt;project root&gt;/ios/ShareExtension/ShareViewController.swift 文件的内容替换为以下代码:

import zikzak_share_handler_ios_models

class ShareViewController: ShareHandlerIosViewController {}
2.8 Xcode 16 和 iOS 故障排除
  • 将Share Extension转换为GroupGroup Share Extension

  • 将Thin Library移动到构建阶段的底部Arrange Build Phases

  • 更新 ios/Runner/Release.xcconfig 文件

#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
#include "Generated.xcconfig"
  • 更新 ios/Runner/Debug.xcconfig 文件
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
#include "Generated.xcconfig"
  • 如果遇到警告

    Warning

    更新 ShareExtension 的构建设置 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER$(inherited),并将其值设置为 No

    Build Setting

3. Android配置

3.1 修改 AndroidManifest.xml

编辑 &lt;project root&gt;/android/app/src/main/AndroidManifest.xml 文件,添加或取消注释你想要支持的 intent 过滤器和元数据:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:name="io.flutter.app.FlutterApplication"
        ...>
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">

            <!-- 如果你想处理共享文本,请添加此过滤器 -->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/*" />
            </intent-filter>

            <!-- 如果你想处理共享图片,请添加此过滤器 -->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="image/*" />
            </intent-filter>

            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="image/*" />
            </intent-filter>

            <!-- 如果你想处理共享视频,请添加此过滤器 -->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="video/*" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="video/*" />
            </intent-filter>

            <!-- 如果你想处理任何类型的文件,请添加此过滤器 -->
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>

            <!-- (可选) 如果你想支持分享到特定目标/对话/快捷方式,请添加这些元数据标签 -->
            <meta-data
                android:name="android.service.chooser.chooser_target_service"
                android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
            <meta-data
                android:name="android.app.shortcuts"
                android:resource="@xml/share_targets" />
        </activity>
    </application>
</manifest>
3.2 (可选) 防止每次共享时打开新Activity

如果你想防止每次共享时打开新的Activity,可以在 MainActivity 的 intent 中添加 android:launchMode="singleTask" 属性。

3.3 (可选) 添加共享建议/快捷方式支持

创建文件 &lt;project root&gt;/android/app/src/main/res/xml/share_targets.xml,并添加以下内容,将 {your.package.identifier} 替换为你的包标识符(例如 wtf.zikzak.zikzak_share_handler_android_example):

<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
    <share-target android:targetClass="{your.package.identifier}.MainActivity">
        <data android:mimeType="*/*" />
        <category android:name="{your.package.identifier}.dynamic_share_target" />
    </share-target>
</shortcuts>

4. 示例代码

以下是一个完整的示例代码,展示了如何使用 zikzak_share_handler 插件来处理共享内容:

import 'dart:io';

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:zikzak_share_handler/zikzak_share_handler.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  void initState() {
    super.initState();
    initPlatformState();
  }

  SharedMedia? media;

  // 平台消息是异步的,因此我们在异步方法中初始化
  Future<void> initPlatformState() async {
    final handler = ShareHandler.instance;
    media = await handler.getInitialSharedMedia();

    handler.sharedMediaStream.listen((SharedMedia sharedMedia) {
      if (!mounted) return;
      setState(() {
        this.media = sharedMedia;
      });
    });

    if (!mounted) return;

    setState(() {
      // _platformVersion = platformVersion;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Share Handler'),
        ),
        body: Center(
          child: ListView(
            children: <Widget>[
              Text("共享到对话标识符: ${media?.conversationIdentifier}"),
              const SizedBox(height: 10),
              Text("共享文本: ${media?.content}"),
              const SizedBox(height: 10),
              Text("共享文件数量: ${media?.attachments?.length}"),
              ...(media?.attachments ?? []).map((attachment) {
                final path = attachment?.path;
                if (path != null && attachment?.type == SharedAttachmentType.image) {
                  return Column(
                    children: [
                      ElevatedButton(
                        onPressed: () {
                          ShareHandlerPlatform.instance.recordSentMessage(
                            conversationIdentifier: "custom-conversation-identifier",
                            conversationName: "John Doe",
                            conversationImageFilePath: path,
                            serviceName: "custom-service-name",
                          );
                        },
                        child: const Text("记录消息"),
                      ),
                      const SizedBox(height: 10),
                      Image.file(File(path)),
                    ],
                  );
                } else {
                  return Text("${attachment?.type} 附件: ${attachment?.path}");
                }
              }).toList(),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter分享处理插件zikzak_share_handler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分享处理插件zikzak_share_handler的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 zikzak_share_handler Flutter 插件的代码示例。zikzak_share_handler 是一个用于处理应用内分享功能的 Flutter 插件,它能够监听并处理来自其他应用的分享意图。

首先,确保你已经在 pubspec.yaml 文件中添加了 zikzak_share_handler 依赖:

dependencies:
  flutter:
    sdk: flutter
  zikzak_share_handler: ^最新版本号  # 请替换为实际最新版本号

然后运行 flutter pub get 来获取依赖。

接下来,在你的 Flutter 应用中实现分享处理功能。以下是一个简单的示例,展示如何设置和使用 zikzak_share_handler

主文件 main.dart

import 'package:flutter/material.dart';
import 'package:zikzak_share_handler/zikzak_share_handler.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Share Handler Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ShareHandlerScreen(),
    );
  }
}

class ShareHandlerScreen extends StatefulWidget {
  @override
  _ShareHandlerScreenState createState() => _ShareHandlerScreenState();
}

class _ShareHandlerScreenState extends State<ShareHandlerScreen> {
  @override
  void initState() {
    super.initState();
    // 初始化并监听分享事件
    ZikzakShareHandler.instance.initialize().then((_) {
      ZikzakShareHandler.instance.onShareReceived.listen((ShareData data) {
        // 处理接收到的分享数据
        setState(() {
          _handleShareData(data);
        });
      });
    });
  }

  void _handleShareData(ShareData data) {
    // 显示接收到的分享内容
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('Received share: ${data.text ?? 'No text provided'}'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Share Handler Example'),
      ),
      body: Center(
        child: Text('Waiting for share...'),
      ),
    );
  }
}

Android 配置

android/app/src/main/AndroidManifest.xml 文件中,确保你的应用能够接收 ACTION_SEND 意图:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <!-- 添加以下 intent-filter 以接收分享意图 -->
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

iOS 配置

对于 iOS,你需要在 Info.plist 文件中添加相应的配置以允许应用接收分享。不过,zikzak_share_handler 插件在 iOS 上的配置相对简单,通常不需要手动修改 Info.plist,除非你有特殊需求。

运行应用

现在你可以运行你的 Flutter 应用,并从其他应用分享文本到你的应用。一旦分享成功,你的应用将显示一个 SnackBar,包含接收到的分享内容。

这个示例展示了如何使用 zikzak_share_handler 来处理应用内分享功能。根据实际需求,你可以进一步扩展和处理不同类型的分享数据(如图片、链接等)。

回到顶部