From af3e8d5d3cf040831c10cbf4dfb86fecf192ab4c Mon Sep 17 00:00:00 2001 From: eu Date: Mon, 9 Dec 2024 15:31:12 -0500 Subject: [PATCH 1/6] fix(share_plus): #1643 fallback for shareXFiles() to use download on web --- .../share_plus/example/lib/main.dart | 32 ++++++++++-------- .../share_plus/lib/src/share_plus_web.dart | 33 +++++++++++++++++-- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index 704e2cf955..fc55d655b8 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -260,20 +260,26 @@ class DemoAppState extends State { 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) { diff --git a/packages/share_plus/share_plus/lib/src/share_plus_web.dart b/packages/share_plus/share_plus/lib/src/share_plus_web.dart index 45d7968aee..9868974f71 100644 --- a/packages/share_plus/share_plus/lib/src/share_plus_web.dart +++ b/packages/share_plus/share_plus/lib/src/share_plus_web.dart @@ -217,16 +217,43 @@ 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}', + error: '$name: $message', ); + if (name case 'NotAllowedError') { + return _download(files, fileNameOverrides); + } - throw Exception('Navigator.share() failed: ${e.message}'); + throw Exception('Navigator.share() failed: $message'); + } + } + + Future _download(List files, List? fileNameOverrides) async { + 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 share files', error: error); + throw Exception('Failed to to share files: $error'); } } From 2603f668b790843d0165908477549460b55b6efb Mon Sep 17 00:00:00 2001 From: eu Date: Mon, 9 Dec 2024 16:48:36 -0500 Subject: [PATCH 2/6] dart format --- packages/share_plus/share_plus/example/lib/main.dart | 2 +- packages/share_plus/share_plus/lib/src/share_plus_web.dart | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index fc55d655b8..3126e00caa 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -275,7 +275,7 @@ class DemoAppState extends State { ); scaffoldMessenger.showSnackBar(getResultSnackBar(shareResult)); - } catch(e) { + } catch (e) { scaffoldMessenger.showSnackBar( SnackBar(content: Text('Error: $e')), ); diff --git a/packages/share_plus/share_plus/lib/src/share_plus_web.dart b/packages/share_plus/share_plus/lib/src/share_plus_web.dart index 9868974f71..4afa62c4e5 100644 --- a/packages/share_plus/share_plus/lib/src/share_plus_web.dart +++ b/packages/share_plus/share_plus/lib/src/share_plus_web.dart @@ -236,7 +236,8 @@ class SharePlusWebPlugin extends SharePlatform { } } - Future _download(List files, List? fileNameOverrides) async { + Future _download( + List files, List? fileNameOverrides) async { try { for (final (index, file) in files.indexed) { final bytes = await file.readAsBytes(); From 98d33e7ec446d03725de045b039b27b9ee99c98e Mon Sep 17 00:00:00 2001 From: eu Date: Wed, 11 Dec 2024 21:18:35 -0500 Subject: [PATCH 3/6] fix(share_plus): #1643 fallback for shareXFiles() to use download on web --- .../share_plus/lib/src/share_plus_web.dart | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/share_plus/share_plus/lib/src/share_plus_web.dart b/packages/share_plus/share_plus/lib/src/share_plus_web.dart index 4afa62c4e5..67ca30a223 100644 --- a/packages/share_plus/share_plus/lib/src/share_plus_web.dart +++ b/packages/share_plus/share_plus/lib/src/share_plus_web.dart @@ -15,6 +15,9 @@ import 'package:web/web.dart'; class SharePlusWebPlugin extends SharePlatform { final UrlLauncherPlatform urlLauncher; + /// Whether to fall back to download files if Navigator.share() fails. + static bool downloadFallbackEnabled = true; + /// Registers this class as the default instance of [SharePlatform]. static void registerWith(Registrar registrar) { SharePlatform.instance = SharePlusWebPlugin(UrlLauncherPlugin()); @@ -204,11 +207,13 @@ 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 { @@ -229,13 +234,23 @@ class SharePlusWebPlugin extends SharePlatform { error: '$name: $message', ); if (name case 'NotAllowedError') { - return _download(files, fileNameOverrides); + return _downloadIfFallbackEnabled( + files, fileNameOverrides, 'Navigator.share() failed: $message'); } throw Exception('Navigator.share() failed: $message'); } } + Future _downloadIfFallbackEnabled( + List files, List? fileNameOverrides, String message) { + if (downloadFallbackEnabled) { + return _download(files, fileNameOverrides); + } else { + throw Exception(message); + } + } + Future _download( List files, List? fileNameOverrides) async { try { From 1ae9aa203b3b56e8e781f3fed449551b231281fd Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 12 Dec 2024 14:27:25 +0100 Subject: [PATCH 4/6] code adjustments and README upgrade --- packages/share_plus/share_plus/README.md | 6 +++ .../share_plus/example/lib/main.dart | 1 + .../share_plus/share_plus/lib/share_plus.dart | 3 ++ .../share_plus/lib/src/share_plus_web.dart | 43 +++++++++++-------- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/packages/share_plus/share_plus/README.md b/packages/share_plus/share_plus/README.md index 1147ca20ff..282bea03a7 100644 --- a/packages/share_plus/share_plus/README.md +++ b/packages/share_plus/share_plus/README.md @@ -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). diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index 3126e00caa..2ffde12987 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -19,6 +19,7 @@ import 'package:share_plus/share_plus.dart'; import 'image_previews.dart'; void main() { + SharePlusWebPlugin.downloadFallbackEnabled = false; runApp(const DemoApp()); } diff --git a/packages/share_plus/share_plus/lib/share_plus.dart b/packages/share_plus/share_plus/lib/share_plus.dart index 52e50d624d..e6e2f64a8b 100644 --- a/packages/share_plus/share_plus/lib/share_plus.dart +++ b/packages/share_plus/share_plus/lib/share_plus.dart @@ -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. diff --git a/packages/share_plus/share_plus/lib/src/share_plus_web.dart b/packages/share_plus/share_plus/lib/src/share_plus_web.dart index 67ca30a223..f4933873fb 100644 --- a/packages/share_plus/share_plus/lib/src/share_plus_web.dart +++ b/packages/share_plus/share_plus/lib/src/share_plus_web.dart @@ -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'; @@ -15,9 +16,6 @@ import 'package:web/web.dart'; class SharePlusWebPlugin extends SharePlatform { final UrlLauncherPlatform urlLauncher; - /// Whether to fall back to download files if Navigator.share() fails. - static bool downloadFallbackEnabled = true; - /// Registers this class as the default instance of [SharePlatform]. static void registerWith(Registrar registrar) { SharePlatform.instance = SharePlusWebPlugin(UrlLauncherPlugin()); @@ -208,12 +206,18 @@ class SharePlusWebPlugin extends SharePlatform { ); return _downloadIfFallbackEnabled( - files, fileNameOverrides, 'Navigator.canShare() is unavailable'); + files, + fileNameOverrides, + 'Navigator.canShare() is unavailable', + ); } if (!canShare) { return _downloadIfFallbackEnabled( - files, fileNameOverrides, 'Navigator.canShare() is false'); + files, + fileNameOverrides, + 'Navigator.canShare() is false', + ); } try { @@ -229,22 +233,21 @@ class SharePlusWebPlugin extends SharePlatform { return _resultDismissed; } - developer.log( - 'Failed to share files', - error: '$name: $message', + return _downloadIfFallbackEnabled( + files, + fileNameOverrides, + 'Navigator.share() failed: $message', ); - if (name case 'NotAllowedError') { - return _downloadIfFallbackEnabled( - files, fileNameOverrides, 'Navigator.share() failed: $message'); - } - - throw Exception('Navigator.share() failed: $message'); } } Future _downloadIfFallbackEnabled( - List files, List? fileNameOverrides, String message) { - if (downloadFallbackEnabled) { + List files, + List? fileNameOverrides, + String message, + ) { + developer.log(message); + if (Share.downloadFallbackEnabled) { return _download(files, fileNameOverrides); } else { throw Exception(message); @@ -252,7 +255,9 @@ class SharePlusWebPlugin extends SharePlatform { } Future _download( - List files, List? fileNameOverrides) async { + List files, + List? fileNameOverrides, + ) async { try { for (final (index, file) in files.indexed) { final bytes = await file.readAsBytes(); @@ -268,8 +273,8 @@ class SharePlusWebPlugin extends SharePlatform { return ShareResult.unavailable; } catch (error) { - developer.log('Failed to share files', error: error); - throw Exception('Failed to to share files: $error'); + developer.log('Failed to download files', error: error); + throw Exception('Failed to to download files: $error'); } } From 80365874b252cc99981ba32b8fc77df84f47631e Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 12 Dec 2024 14:28:21 +0100 Subject: [PATCH 5/6] fix example code --- packages/share_plus/share_plus/example/lib/main.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index 2ffde12987..3fe08511e0 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -19,7 +19,10 @@ import 'package:share_plus/share_plus.dart'; import 'image_previews.dart'; void main() { - SharePlusWebPlugin.downloadFallbackEnabled = false; + // Set `downloadFallbackEnabled` to `false` + // to disable downloading files if `shareXFiles` fails on web. + Share.downloadFallbackEnabled = true; + runApp(const DemoApp()); } From 177344994dd05a5851707c974bfd861e4e5339c2 Mon Sep 17 00:00:00 2001 From: Miguel Beltran Date: Thu, 12 Dec 2024 14:32:15 +0100 Subject: [PATCH 6/6] improved example and logs --- .../share_plus/example/lib/main.dart | 33 +++++++++++-------- .../share_plus/lib/src/share_plus_web.dart | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index 3fe08511e0..913a6e33ce 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -245,20 +245,25 @@ class DemoAppState extends State { 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 { diff --git a/packages/share_plus/share_plus/lib/src/share_plus_web.dart b/packages/share_plus/share_plus/lib/src/share_plus_web.dart index f4933873fb..d0456d327c 100644 --- a/packages/share_plus/share_plus/lib/src/share_plus_web.dart +++ b/packages/share_plus/share_plus/lib/src/share_plus_web.dart @@ -258,6 +258,7 @@ class SharePlusWebPlugin extends SharePlatform { List files, List? fileNameOverrides, ) async { + developer.log('Download files as fallback'); try { for (final (index, file) in files.indexed) { final bytes = await file.readAsBytes();