Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/share_plus/share_plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ package.
Share.shareXFiles([XFile('assets/hello.txt')], text: 'Great picture');
```

File downloading fallback mechanism for web can be disabled by setting:

```dart
Share.downloadFallbackEnabled = false;
```

#### Share Data

You can also share files that you dynamically generate from its data using [`XFile.fromData`](https://pub.dev/documentation/share_plus/latest/share_plus/XFile/XFile.fromData.html).
Expand Down
69 changes: 42 additions & 27 deletions packages/share_plus/share_plus/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import 'package:share_plus/share_plus.dart';
import 'image_previews.dart';

void main() {
// Set `downloadFallbackEnabled` to `false`
// to disable downloading files if `shareXFiles` fails on web.
Share.downloadFallbackEnabled = true;

runApp(const DemoApp());
}

Expand Down Expand Up @@ -241,39 +245,50 @@ class DemoAppState extends State<DemoApp> {
void _onShareXFileFromAssets(BuildContext context) async {
final box = context.findRenderObject() as RenderBox?;
final scaffoldMessenger = ScaffoldMessenger.of(context);
final data = await rootBundle.load('assets/flutter_logo.png');
final buffer = data.buffer;
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
name: 'flutter_logo.png',
mimeType: 'image/png',
),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);

scaffoldMessenger.showSnackBar(getResultSnackBar(shareResult));
try {
final data = await rootBundle.load('assets/flutter_logo.png');
final buffer = data.buffer;
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
name: 'flutter_logo.png',
mimeType: 'image/png',
),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
scaffoldMessenger.showSnackBar(getResultSnackBar(shareResult));
} catch (e) {
scaffoldMessenger.showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}

void _onShareTextAsXFile(BuildContext context) async {
final box = context.findRenderObject() as RenderBox?;
final scaffoldMessenger = ScaffoldMessenger.of(context);
final data = utf8.encode(text);
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
data,
// name: fileName, // Notice, how setting the name here does not work.
mimeType: 'text/plain',
),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
fileNameOverrides: [fileName],
);

scaffoldMessenger.showSnackBar(getResultSnackBar(shareResult));
try {
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
utf8.encode(text),
// name: fileName, // Notice, how setting the name here does not work.
mimeType: 'text/plain',
),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
fileNameOverrides: [fileName],
);

scaffoldMessenger.showSnackBar(getResultSnackBar(shareResult));
} catch (e) {
scaffoldMessenger.showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}

SnackBar getResultSnackBar(ShareResult result) {
Expand Down
3 changes: 3 additions & 0 deletions packages/share_plus/share_plus/lib/share_plus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export 'src/share_plus_windows.dart'
class Share {
static SharePlatform get _platform => SharePlatform.instance;

/// Whether to fall back to downloading files if [shareXFiles] fails on web.
static bool downloadFallbackEnabled = true;

/// Summons the platform's share sheet to share uri.
///
/// Wraps the platform's native share dialog. Can share a URL.
Expand Down
63 changes: 56 additions & 7 deletions packages/share_plus/share_plus/lib/src/share_plus_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:ui';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:meta/meta.dart';
import 'package:mime/mime.dart' show lookupMimeType;
import 'package:share_plus/share_plus.dart';
import 'package:share_plus_platform_interface/share_plus_platform_interface.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';
import 'package:url_launcher_web/url_launcher_web.dart';
Expand Down Expand Up @@ -204,11 +205,19 @@ class SharePlusWebPlugin extends SharePlatform {
error: e,
);

throw Exception('Navigator.canShare() is unavailable');
return _downloadIfFallbackEnabled(
files,
fileNameOverrides,
'Navigator.canShare() is unavailable',
);
}

if (!canShare) {
throw Exception('Navigator.canShare() is false');
return _downloadIfFallbackEnabled(
files,
fileNameOverrides,
'Navigator.canShare() is false',
);
}

try {
Expand All @@ -217,16 +226,56 @@ class SharePlusWebPlugin extends SharePlatform {
// actions is success, but can't get the action name
return ShareResult.unavailable;
} on DOMException catch (e) {
if (e.name case 'AbortError') {
final name = e.name;
final message = e.message;

if (name case 'AbortError') {
return _resultDismissed;
}

developer.log(
'Failed to share files',
error: '${e.name}: ${e.message}',
return _downloadIfFallbackEnabled(
files,
fileNameOverrides,
'Navigator.share() failed: $message',
);
}
}

throw Exception('Navigator.share() failed: ${e.message}');
Future<ShareResult> _downloadIfFallbackEnabled(
List<XFile> files,
List<String>? fileNameOverrides,
String message,
) {
developer.log(message);
if (Share.downloadFallbackEnabled) {
return _download(files, fileNameOverrides);
} else {
throw Exception(message);
}
}

Future<ShareResult> _download(
List<XFile> files,
List<String>? fileNameOverrides,
) async {
developer.log('Download files as fallback');
try {
for (final (index, file) in files.indexed) {
final bytes = await file.readAsBytes();

final anchor = document.createElement('a') as HTMLAnchorElement
..href = Uri.dataFromBytes(bytes).toString()
..style.display = 'none'
..download = fileNameOverrides?.elementAt(index) ?? file.name;
document.body!.children.add(anchor);
anchor.click();
anchor.remove();
}

return ShareResult.unavailable;
} catch (error) {
developer.log('Failed to download files', error: error);
throw Exception('Failed to to download files: $error');
}
}

Expand Down