share_intent_package 1.0.30
share_intent_package: ^1.0.30 copied to clipboard
Zero-configuration Flutter share intent plugin. Receive shared content from other apps with one-command setup. Fully automated iOS ShareExtension + Android intent filters.
example/lib/main.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:share_intent_package/share_intent_package.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Share Intent Test',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const ShareTestPage(),
);
}
}
class ShareTestPage extends StatefulWidget {
const ShareTestPage({super.key});
@override
State<ShareTestPage> createState() => _ShareTestPageState();
}
class _ShareTestPageState extends State<ShareTestPage> with SingleTickerProviderStateMixin {
late TabController _tabController;
StreamSubscription? _subscription;
SharedData? _sharedData;
String _status = 'Waiting for shared content...';
// Share Out Controllers
final TextEditingController _textController = TextEditingController(
text: 'Hello from Share Intent Package!',
);
final TextEditingController _fileContentController = TextEditingController(
text: 'This is a temporary text file shared from the example app.',
);
final TextEditingController _mixedTextController = TextEditingController(
text: 'Check out this file!',
);
final TextEditingController _mixedFileContentController = TextEditingController(
text: 'Content of the mixed share file.',
);
String _shareOutStatus = 'Idle';
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
_initSharing();
}
Future<void> _initSharing() async {
try {
// Initialize the plugin
await ShareIntentPackage.instance.init();
// Get initial sharing data (if app was opened via share)
final initial = await ShareIntentPackage.instance.getInitialSharing();
if (initial != null && initial.hasContent) {
setState(() {
_sharedData = initial;
_status = 'Received initial shared content!';
});
}
// Listen for shares while app is running
_subscription = ShareIntentPackage.instance.getMediaStream().listen((
data,
) {
setState(() {
_sharedData = data;
_status = 'Received new shared content!';
});
});
setState(() {
if (_sharedData == null) {
_status = 'Plugin initialized. Share something to this app!';
}
});
} catch (e) {
setState(() {
_status = 'Error initializing: $e';
});
}
}
@override
void dispose() {
_tabController.dispose();
_subscription?.cancel();
_textController.dispose();
_fileContentController.dispose();
_mixedTextController.dispose();
_mixedFileContentController.dispose();
super.dispose();
}
Future<File> _createTempFile(String name, String content) async {
final tempDir = Directory.systemTemp;
final file = File('${tempDir.path}/$name');
await file.writeAsString(content);
return file;
}
Future<void> _shareText() async {
final text = _textController.text.trim();
if (text.isEmpty) {
setState(() => _shareOutStatus = 'Error: Share text cannot be empty');
return;
}
setState(() => _shareOutStatus = 'Sharing text...');
try {
await ShareIntentPackage.shareText(text);
setState(() => _shareOutStatus = 'Text shared successfully!');
} catch (e) {
setState(() => _shareOutStatus = 'Error sharing text: $e');
}
}
Future<void> _shareFile() async {
final content = _fileContentController.text;
setState(() => _shareOutStatus = 'Creating temp file...');
try {
final file = await _createTempFile('share_test_file.txt', content);
setState(() => _shareOutStatus = 'Sharing file...');
await ShareIntentPackage.shareFiles([file.path]);
setState(() => _shareOutStatus = 'File shared successfully!');
} catch (e) {
setState(() => _shareOutStatus = 'Error sharing file: $e');
}
}
Future<void> _shareMixed() async {
final text = _mixedTextController.text.trim();
final content = _mixedFileContentController.text;
setState(() => _shareOutStatus = 'Creating temp file for mixed share...');
try {
final file = await _createTempFile('share_test_mixed.txt', content);
setState(() => _shareOutStatus = 'Sharing mixed content...');
await ShareIntentPackage.shareContent(
text: text.isEmpty ? null : text,
filePaths: [file.path],
);
setState(() => _shareOutStatus = 'Mixed content shared successfully!');
} catch (e) {
setState(() => _shareOutStatus = 'Error sharing mixed content: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Share Intent Test'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(icon: Icon(Icons.download), text: 'Receive Share (In)'),
Tab(icon: Icon(Icons.upload), text: 'Send Share (Out)'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
_buildReceiveShareTab(),
_buildSendShareTab(),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_sharedData = null;
_status = 'Cleared. Waiting for shared content...';
_shareOutStatus = 'Idle';
});
},
tooltip: 'Clear',
child: const Icon(Icons.clear),
),
);
}
Widget _buildReceiveShareTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status Card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Status',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(_status),
],
),
),
),
const SizedBox(height: 16),
// Shared Data Card
if (_sharedData != null) ...[
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Shared Content',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 16),
// Content Type
_buildInfoRow('Type', _getContentType()),
const SizedBox(height: 8),
// MIME Type
if (_sharedData!.mimeType != null)
_buildInfoRow('MIME Type', _sharedData!.mimeType!),
// Text Content
if (_sharedData!.text != null) ...[
const SizedBox(height: 8),
_buildInfoRow('Text', _sharedData!.text!),
],
// File Paths
if (_sharedData!.filePaths.isNotEmpty) ...[
const SizedBox(height: 16),
Text(
'Files (${_sharedData!.filePaths.length}):',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
..._sharedData!.filePaths.map((path) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
path,
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 8),
// Show image preview if it's an image
if (_isImageFile(path))
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(path),
height: 200,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 100,
color: Colors.grey[300],
child: const Center(
child: Text('Could not load image'),
),
);
},
),
),
const SizedBox(height: 16),
],
);
}),
],
],
),
),
),
] else ...[
Card(
child: Padding(
padding: const EdgeInsets.all(32),
child: Center(
child: Column(
children: [
Icon(Icons.share, size: 64, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No shared content yet',
style: Theme.of(context).textTheme.titleMedium
?.copyWith(color: Colors.grey[600]),
),
const SizedBox(height: 8),
Text(
'Share an image, text, or URL from another app to test',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: Colors.grey[500]),
),
],
),
),
),
),
],
const SizedBox(height: 24),
// Instructions Card
Card(
color: Colors.blue[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info_outline, color: Colors.blue[700]),
const SizedBox(width: 8),
Text(
'How to Test Receive',
style: Theme.of(context).textTheme.titleMedium
?.copyWith(color: Colors.blue[700]),
),
],
),
const SizedBox(height: 12),
Text(
'1. Open your photo gallery or browser\n'
'2. Select an image, video, or copy a URL\n'
'3. Tap the share button\n'
'4. Select this app from the share menu\n'
'5. The shared content will appear here',
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
),
],
),
);
}
Widget _buildSendShareTab() {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Operation Status Card
Card(
color: Colors.green[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.sync, color: Colors.green[700]),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Share Out Action Status',
style: Theme.of(context).textTheme.titleSmall
?.copyWith(color: Colors.green[800]),
),
Text(
_shareOutStatus,
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
),
],
),
),
),
const SizedBox(height: 16),
// 1. Share Text
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'1. Share Text / URL',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
TextField(
key: const Key('share_text_field'),
controller: _textController,
decoration: const InputDecoration(
labelText: 'Text to Share',
border: OutlineInputBorder(),
),
maxLines: 2,
),
const SizedBox(height: 12),
ElevatedButton.icon(
key: const Key('share_text_button'),
onPressed: _shareText,
icon: const Icon(Icons.text_fields),
label: const Text('Share Text'),
),
],
),
),
),
const SizedBox(height: 16),
// 2. Share File
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'2. Share File (Temporary Text File)',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
TextField(
key: const Key('share_file_content_field'),
controller: _fileContentController,
decoration: const InputDecoration(
labelText: 'Temp File Content',
border: OutlineInputBorder(),
),
maxLines: 2,
),
const SizedBox(height: 12),
ElevatedButton.icon(
key: const Key('share_file_button'),
onPressed: _shareFile,
icon: const Icon(Icons.insert_drive_file),
label: const Text('Create & Share File'),
),
],
),
),
),
const SizedBox(height: 16),
// 3. Share Mixed
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'3. Share Mixed (Text + File)',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
TextField(
key: const Key('share_mixed_text_field'),
controller: _mixedTextController,
decoration: const InputDecoration(
labelText: 'Mixed Share Text',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 8),
TextField(
key: const Key('share_mixed_content_field'),
controller: _mixedFileContentController,
decoration: const InputDecoration(
labelText: 'Mixed Temp File Content',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
ElevatedButton.icon(
key: const Key('share_mixed_button'),
onPressed: _shareMixed,
icon: const Icon(Icons.all_inclusive),
label: const Text('Share Mixed Content'),
),
],
),
),
),
],
),
);
}
Widget _buildInfoRow(String label, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(child: Text(value)),
],
);
}
String _getContentType() {
if (_sharedData == null) return 'Unknown';
if (_sharedData!.isImage) return 'Image';
if (_sharedData!.isVideo) return 'Video';
if (_sharedData!.isUrl) return 'URL';
if (_sharedData!.isText) return 'Text';
if (_sharedData!.isMedia) return 'File';
return 'Unknown';
}
bool _isImageFile(String path) {
final lower = path.toLowerCase();
return lower.endsWith('.jpg') ||
lower.endsWith('.jpeg') ||
lower.endsWith('.png') ||
lower.endsWith('.gif') ||
lower.endsWith('.webp') ||
lower.endsWith('.bmp');
}
}