share_receiver 1.0.0
share_receiver: ^1.0.0 copied to clipboard
A Flutter plugin for iOS and Android to handle incoming shared text or media from other apps.
🌿 Share Receiver 🌿
A powerful and easy-to-use Flutter plugin that enables your iOS and Android apps to seamlessly receive and handle shared text, images, videos, and files from other applications using native share sheets.
Guide #
Installation #
Add share_receiver as a dependency in your pubspec.yaml file:
flutter pub add share_receiver
Configuration #
Android
- Edit your Android Manifest file, located in
android/app/src/main/AndroidManifest.xmland add/uncomment the intent filters and meta data that you want to support:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="share_receiver_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- ... -->
<!-- Intent filters for share functionality -->
<!--TODO: Add this filter if you want to handle shared text-->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<!--TODO: Add this filter if you want to handle shared images-->
<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>
<!--TODO: Add this filter if you want to handle shared videos-->
<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>
<!--TODO: Add this filter if you want to handle any type of file-->
<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>
</activity>
<!-- ... -->
</application>
<!-- ... -->
</manifest>
Note
If you want to prevent incoming shares from opening a new activity each time, add/edit the attribute android:launchMode="singleTask" to your MainActivity intent inside your AndroidManifest.xml file.
iOS
- Edit your iOS Info.plist file, located in
ios/Runner/Info.plist. It registers your app to open via a deep link that will be launched from the Share Extension.
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it in 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>
- Create Share Extension
- In Xcode, go to the menu and select File -> New -> Target -> choose "Share Extension"
- Give it the name
ShareExtensionand save
- Go to Build Phases of your
Runnertarget and moveEmbed Foundation Extensionto the top ofThin Binary. - Make the following edits to
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>
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it in Build Settings -> User-Defined -->
<!-- <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> -->
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<!-- The TRUEPREDICATE NSExtensionActivationRule that only works in development mode -->
<!-- <string>TRUEPREDICATE</string> -->
<!-- Add a new rule below will allow sharing one or more file of any type, url, or text content, You can modify these rules to your liking for which types of share content, as well as how many your app can handle -->
<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>
- Add a group identifier to both the
RunnerandShareExtensionTargets- In Xcode, select Runner -> Targets -> Runner -> Signing & Capabilities
- Click the '+' button and select
App Groups - Add a new group (default is your bundle identifier prefixed by
group.. ex.group.com.company.app) - Repeat those above 3 steps inside of the
ShareExtensiontarget adding/selecting the same group id
- (Optional) If you made a custom group identifier that isn't your bundle identifier prefixed by 'group.', make sure to add a custom build setting variable that is referenced in your ShareExtension's Info.plist file.
- Go to Targets -> ShareExtension -> Build Settings
- Click the '+' icon and select
Add User-Defined Setting - Give it the key
CUSTOM_GROUP_IDand the value of the app group identifier that you gave to both targets in the previous step - Repeat the above steps for the
Runnertarget
- If your project use
Swift Package Managerplease skip this step, Otherwise if useCocoaPodsrequired add the following code insideios/Podfilewithin thetarget 'Runner' doblock, and then runpod installinside theiosdirectory.
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# share_receiver start
target 'ShareExtension' do
inherit! :search_paths
pod 'share_receiver', :path => '.symlinks/plugins/share_receiver/ios/share_receiver'
end
# share_receiver end
end
- In Xcode, replace the contents of
ShareExtension/ShareViewController.swiftwith the following code. The share extension doesn't launch a UI of its own, instead it serializes the shared content/media and saves it to the groups shared preferences, then opens a deep link into the main app so your flutter/dart code can then read the serialized data and handle it accordingly.
import share_receiver
class ShareViewController: ShareReceiverServiceViewController {}
Example #
import 'package:flutter/material.dart';
import 'package:share_receiver/share_receiver.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
SharedData? _sharedData;
@override
void initState() {
super.initState();
_initShareReceiver();
}
@override
void dispose() {
ShareReceiver.instance.dispose();
super.dispose();
}
Future<void> _initShareReceiver() async {
// Get initial sharing data (if app was opened via share)
final initial = await ShareReceiver.instance.getInitialSharing();
if (initial != null) {
print('Received initial share data: $initial');
setState(() => _sharedData = initial);
// Clear the initial data
ShareReceiver.instance.clear();
}
// Listen for shares while app is running
ShareReceiver.instance.getMediaStream().listen((data) {
print('Received share data: $data');
setState(() => _sharedData = data);
// Clear the received data
ShareReceiver.instance.clear();
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Share Receiver Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisSize: .min,
spacing: 8.0,
children: [
Text('Shared data: ${_sharedData?.toString() ?? 'No data'}'),
],
),
),
),
),
);
}
}
See the example here for runnable project of various usages.
Bugs or Requests #
If you encounter any problems feel free to open an issue. If you feel the library is missing a feature, please raise a ticket on GitHub and I'll look into it. Pull request are also welcome.
See Contributing.md.
Support #
Don't forget to give it a like 👍 or a star ⭐
