plus_plugins
plus_plugins copied to clipboard
[Bug]: share_plus Share.shareXFiles() is not working on web
Platform
Chrome web browser on MacOS
Plugin
share_plus
Version
latest from the main
Flutter SDK
3.7.8
Steps to reproduce
- Checkout latetst sources from main
- Open project in Android Studio
- Select Chrome (web) as target and launch share_plus/example app
- Click on the "Share XFile from Assets" button
- The following exception is thrown but the plugin readme says "it falls back to downloading the shared files" when Web Share API is not available.
Code Sample
The `share_plus/example` app from https://github.com/fluttercommunity/plus_plugins/tree/main/packages/share_plus/share_plus
Logs
Debug service listening on ws://127.0.0.1:50601/BFGEsW3RKXM=/ws
TypeError: this.share is not a function
dart-sdk/lib/html/dart2js/html_dart2js.dart 22933:49 share]
packages/share_plus/src/share_plus_web.dart 105:22 shareXFiles
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 45:50 <fn>
dart-sdk/lib/async/zone.dart 1660:54 runUnary
dart-sdk/lib/async/future_impl.dart 147:18 handleValue
dart-sdk/lib/async/future_impl.dart 767:44 handleValueCallback
dart-sdk/lib/async/future_impl.dart 796:13 _propagateToListeners
dart-sdk/lib/async/future_impl.dart 558:7 [_complete]
dart-sdk/lib/async/stream_pipe.dart 61:11 _cancelAndValue
dart-sdk/lib/async/stream.dart 1587:7 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 367:37 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 372:39 dcall
dart-sdk/lib/html/dart2js/html_dart2js.dart 37367:58 <fn>
Flutter Doctor
.../flutter doctor --verbose
[✓] Flutter (Channel stable, 3.7.8, on macOS 12.6.1 21G217 darwin-arm64, locale en-CA)
• Flutter version 3.7.8 on channel stable at .../flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 90c64ed42b (5 days ago), 2023-03-21 11:27:08 -0500
• Engine revision 9aa7816315
• Dart version 2.19.5
• DevTools version 2.20.1
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14C18
• CocoaPods version 1.11.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
Checklist before submitting a bug
- [X] I Google'd a solution and I couldn't find it
- [X] I searched on StackOverflow for a solution and I couldn't find it
- [X] I read the README.md file of the plugin
- [X] I'm using the latest version of the plugin
- [X] All dependencies are up to date with
flutter pub upgrade
- [X] I did a
flutter clean
- [X] I tried running the example project
I got the same error message when I create a XFile with image data, and then share it by 'shareXFiles' function
I got the same error message when I create a XFile with image data, and then share it by 'shareXFiles' function
I got the same error message on Chrome 113 on iOS 16.4.1 (iPhone):
TypeError: this.share is not a function. (In 'this.share(data_dict)', 'this.share' is undefined)
Same here! I am using XFile.fromData(/* some image byte Uint8List */)
if that helps finding the cause.
Same issue here!
Sample code that uses PictureRecorder to generate a png from canvas
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas canvas = Canvas(recorder);
Size exportSize = Size.square(size);
paint!.painter!.paint(canvas, exportSize);
ui.Image renderedImage = await recorder
.endRecording()
.toImage(exportSize.width.floor(), exportSize.height.floor());
ByteData? pngBytes =
await renderedImage.toByteData(format: ui.ImageByteFormat.png);
Share.shareXFiles(
[
XFile.fromData(
pngBytes!.buffer
.asUint8List(pngBytes.offsetInBytes, pngBytes.lengthInBytes),
name: 'qr-code.png',
mimeType: 'image/png',
)
],
);
followed the example https://github.com/fluttercommunity/plus_plugins/blob/b6888edcf224b9d5a1dc15716881b6e1a7114ef5/packages/share_plus/share_plus/example/lib/main.dart#L243-L252
still getting
Uncaught (in promise) TypeError: this.share is not a function
at [dartx.share] (html_dart2js.dart:22933:49)
at share_plus_web.SharePlusWebPlugin.new.shareXFiles (share_plus_web.dart:105:22)
at shareXFiles.next (<anonymous>)
at async_patch.dart:45:50
at _RootZone.runUnary (zone.dart:1660:54)
at _FutureListener.thenAwait.handleValue (future_impl.dart:147:18)
at handleValueCallback (future_impl.dart:767:44)
at _Future._propagateToListeners (future_impl.dart:796:13)
at [_complete] (future_impl.dart:558:7)
at Object._cancelAndValue (stream_pipe.dart:61:11)
at stream.dart:1587:7
at Object._checkAndCall (operations.dart:367:37)
at Object.dcall (operations.dart:372:39)
Tried on Safari and Edge also, none worked.
Seconding this is an issue for me.
My code sample:
var shareButton = TextButton( style: TextButton.styleFrom( backgroundColor: FauraColors().babiOrange(), padding: const EdgeInsets.all(5.0), ), onPressed: () async { Share.shareXFiles( [ await XFile.fromData(await pdfLogic.returnActionItemPDF( widget.actionItems, PdfPageFormat.letter)), ], subject: "Home Wildfire Mitigation Plan", ); }, child: Text( "Share PDF.", style: FauraTheme().fauraTextTheme().bodySmall, ));
Any plans for fixing or workarounds?
I have the same problem, has anyone solved it?
I think the issue is that web browsers do not have a native share menu like mobile devices have. However, you can download single files. Here is an example:
await XFile.fromData(
Uint8List.fromList(/* some mp3 data */),
mimeType: 'audio/mpeg',
).saveTo('my-file-name.mp3');
Does anyone knows how to fix it ?
- Tried on Desktop browser -> got error, NoSuchMethodError: tried to call a non-function, such as null: 'this.share'.
- Tried on Mobile browser (Safari) -> got this white UI pop up
@dongnqdev would you mind sharing your implementation?
@xonaman Yes, This is my code. If you wonder where is the bytes came from. It was data converted from Widget to Image. And it works well on android and iOS.
Btw, I have a question doesn't relates to this topic. But does ShareResult.raw provides us any information about the option that user have chosen? For example, save to device or name of the selected application (twitter x,....).
Thanks
Future<void> saveAndShare(
Uint8List bytes, WidgetRef ref, bool? isFirstShared) async {
late final XFile file;
if (kIsWeb) {
file = XFile.fromData(bytes);
} else {
final directory = await getApplicationDocumentsDirectory();
final image = File('${directory.path}/xlp.png');
image.writeAsBytesSync(bytes);
file = XFile(image.path);
}
final result = await Share.shareXFiles([file],
subject: BStrings.current.welcomeToBlockx,
text: BStrings.current.welcomeToBlockx);
}
This is my doctor -v, and I'm using share_plus: ^7.2.1
[!] Flutter (Channel [user-branch], 3.13.9, on macOS 13.6.3 22G436
darwin-x64, locale en-US)
! Flutter version 3.13.9 on channel [user-branch] at
/Users/developerblockx/Documents/development/flutter
Currently on an unknown channel. Run `flutter channel` to
switch to an official channel.
If that doesn't fix the issue, reinstall Flutter by following
instructions at https://flutter.dev/docs/get-started/install.
! Upstream repository unknown source is not a standard remote.
Set environment variable "FLUTTER_GIT_URL" to unknown source
to dismiss this error.
• Framework revision d211f42860 (5 months ago), 2023-10-25
13:42:25 -0700
• Engine revision 0545f8705d
• Dart version 3.1.5
• DevTools version 2.25.0
• If those were intentional, you can disregard the above
warnings; however it is recommended to use "git" directly to
perform update checks and upgrades.
[✓] Android toolchain - develop for Android devices (Android SDK
version 34.0.0)
• Android SDK at /Users/developerblockx/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• Java binary at: /Applications/Android
Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build
17.0.7+0-17.0.7b1000.6-10550314)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.2)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15C500b
• CocoaPods version 1.14.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google
Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build
17.0.7+0-17.0.7b1000.6-10550314)
[✓] VS Code (version 1.85.1)
• VS Code at /Users/developerblockx/Downloads/Visual Studio
Code.app/Contents
• Flutter extension version 3.84.0
[✓] Connected device (2 available)
• macOS (desktop) • macos • darwin-x64 • macOS 13.6.3 22G436 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 122.0.6261.112
[✓] Network resources
• All expected network resources are available.
The documentation mentions the following:
/// Share [XFile] objects.
///
/// Remarks for the web implementation:
/// This uses the [Web Share API](https://web.dev/web-share/) if it's
/// available. Otherwise, uncaught Errors will be thrown.
/// See [Can I Use - Web Share API](https://caniuse.com/web-share) to
/// understand which browsers are supported. This builds on the
/// [`cross_file`](https://pub.dev/packages/cross_file) package.
- https://web.dev/web-share/
- https://caniuse.com/web-share
The code essentially all does is to call to dart:html
share
method with a list of "web" files.
https://github.com/fluttercommunity/plus_plugins/blob/main/packages/share_plus/share_plus/lib/src/share_plus_web.dart#L105
One improvement that could be done is using the canShare
method from the Navigator to check if those files are "shareable": https://web.dev/articles/web-share#sharing_files as it looks like not all types of files are supported
One thing you can try as well is setting the mimeType
for all files, just in case that helps the browser.
@miquelbeltran Thank for your response. I did check the version of safari before posting my issue. My iPhone is running 17.3.1, so the safari on my phone is supported.
- I will try your suggestion and post the result here. Thanks for your help
@miquelbeltran Hi Miquelbentran I have tried to specify the mimeType, but still got the same problem. This is my code. About Navigator class, I couldn't find canShare method.
Future<void> saveAndShare(
Uint8List bytes, WidgetRef ref, bool? isFirstShared) async {
late final XFile file;
if (kIsWeb) {
file = XFile.fromData(bytes, mimeType: 'mage/png');<==== HERE
} else {
final directory = await getApplicationDocumentsDirectory();
final image = File('${directory.path}/xlp.png');
image.writeAsBytesSync(bytes);
file = XFile(image.path);
}
final result = await Share.shareXFiles([file],
subject: BStrings.current.welcomeToBlockx,
text: BStrings.current.welcomeToBlockx);
}
This is StackTrace
TRACE 1: dart-sdk/lib/html/dart2js/html_dart2js.dart 22831:49 share]
packages/share_plus/src/share_plus_web.dart 105:22 shareXFiles
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 45:50 <fn>
dart-sdk/lib/async/zone.dart 1407:47 _rootRunUnary
dart-sdk/lib/async/zone.dart 1308:19 runUnary
dart-sdk/lib/async/future_impl.dart 156:18 handleValue
dart-sdk/lib/async/future_impl.dart 840:44 handleValueCallback
dart-sdk/lib/async/future_impl.dart 869:13 _propagateToListeners
dart-sdk/lib/async/future_impl.dart 632:7 [_complete]
dart-sdk/lib/async/stream_pipe.dart 61:11 _cancelAndValue
dart-sdk/lib/async/stream.dart 1581:7 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 574:37 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 579:39 dcall
dart-sdk/lib/html/dart2js/html_dart2js.dart 37263:58 <fn>
dart-sdk/lib/async/zone.dart 1415:13 _rootRunUnary
dart-sdk/lib/async/zone.dart 1308:19 runUnary
dart-sdk/lib/async/zone.dart 1217:7 runUnaryGuarded
dart-sdk/lib/async/zone.dart 1254:26 <fn>
dart-sdk/lib/html/dart2js/html_dart2js.dart 22831:49 share]
packages/share_plus/src/share_plus_web.dart 105:22 shareXFiles
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 45:50 <fn>
dart-sdk/lib/async/zone.dart 1407:47 _rootRunUnary
dart-sdk/lib/async/zone.dart 1308:19 runUnary
dart-sdk/lib/async/future_impl.dart 156:18 handleValue
dart-sdk/lib/async/future_impl.dart 840:44 handleValueCallback
dart-sdk/lib/async/future_impl.dart 869:13 _propagateToListeners
dart-sdk/lib/async/future_impl.dart 632:7 [_complete]
dart-sdk/lib/async/stream_pipe.dart 61:11 _cancelAndValue
dart-sdk/lib/async/stream.dart 1581:7 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 574:37 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 579:39 dcall
dart-sdk/lib/html/dart2js/html_dart2js.dart 37263:58 <fn>
dart-sdk/lib/async/zone.dart 1415:13 _rootRunUnary
dart-sdk/lib/async/zone.dart 1308:19 runUnary
dart-sdk/lib/async/zone.dart 1217:7 runUnaryGuarded
dart-sdk/lib/async/zone.dart 1254:26 <fn>
That doesn't look correct --> 'mage/png'
it should be 'image/png'
but anyway, this looks like an issue with the underlying browser/js rather than something the plugin can fix
@miquelbeltran Yes, I tried the 'image/png'
, i missed the letter 'I'
when copy the code. Thank you for clarifying. I have tried on safari (Desktop), chrome (Desktop), chome (IOS), Safari (IOS), chrome (Android). They all got the same problem, the pop up is white.
same issue, flutter: 3.16.9
You can use MemoryFileSystem for web and LocalFileSystem for mobile from https://pub.dev/packages/file.
final FileSystem _fileSystem = kIsWeb ? MemoryFileSystem() : const LocalFileSystem();
See their example to create a file https://github.com/google/file.dart/blob/master/packages/file/example/main.dart.
You can than pass this file's path and basename into an XFile.
XFile(
file.path,
name: file.basename,
)
But for now the Share sheet for me is only shown in Safari (not Chrome, not Firefox).
So as a fallback you can have a download action.
import 'package:universal_html/html.dart' as html;
final result = await Share.shareXFiles(
[
XFile(
file.path,
name: file.basename,
),
],
subject: "Subject",
text:
"Text",
// required for iPads, see https://pub.dev/packages/share_plus
sharePositionOrigin: (context.findRenderObject() as RenderBox?)!
.localToGlobal(Offset.zero) &
(context.findRenderObject() as RenderBox?)!.size,
);
// Web fallback if Share API is not available
if (result.status == ShareResultStatus.unavailable && kIsWeb) {
final url =
"data:application/pdf;base64,${base64Encode(await file.readAsBytes())}";
try {
final html.AnchorElement anchorElement =
html.AnchorElement(href: url);
anchorElement.download = model.fetchedPDFFile!.basename;
anchorElement.click();
anchorElement.remove();
} catch (e) {
await launchUrl(
Uri.parse(
url,
),
);
}
}
That doesn't look correct -->
'mage/png'
it should be'image/png'
but anyway, this looks like an issue with the underlying browser/js rather than something the plugin can fix
The documentation states On web, this uses the [Web Share API](https://web.dev/web-share/) if it's available. Otherwise it falls back to downloading the shared files.
However, this fallback does not seem to be working, which in my opinion is a bug. It can also be easily fixed with proper fallback handling.
Below is some code that works great for me, using the universal html package.
Future<void> _downloadWeb(XFile file) async {
final bytes = await file.readAsBytes();
final blob = html.Blob([bytes]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = file.name;
html.document.body!.children.add(anchor);
anchor.click();
html.document.body!.children.remove(anchor);
html.Url.revokeObjectUrl(url);
}
However, this fallback does not seem to be working, which in my opinion is a bug.
Yeah, looking at the code seems that it was removed (or even never implemented?).
Anyway, if you want, you can submit a PR with the change... Or the README should be corrected.