/
share_plus_web.dart
132 lines (117 loc) · 3.95 KB
/
share_plus_web.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:mime/mime.dart' show lookupMimeType;
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';
/// The web implementation of [SharePlatform].
class SharePlusWebPlugin extends SharePlatform {
final UrlLauncherPlatform urlLauncher;
/// Registers this class as the default instance of [SharePlatform].
static void registerWith(Registrar registrar) {
SharePlatform.instance = SharePlusWebPlugin(UrlLauncherPlugin());
}
final html.Navigator _navigator;
/// A constructor that allows tests to override the window object used by the plugin.
SharePlusWebPlugin(
this.urlLauncher, {
@visibleForTesting html.Navigator? debugNavigator,
}) : _navigator = debugNavigator ?? html.window.navigator;
/// Share text
@override
Future<void> share(
String text, {
String? subject,
Rect? sharePositionOrigin,
}) async {
try {
await _navigator.share({'title': subject, 'text': text});
} on NoSuchMethodError catch (_) {
//Navigator is not available or the webPage is not served on https
final queryParameters = {
if (subject != null) 'subject': subject,
'body': text,
};
// see https://github.com/dart-lang/sdk/issues/43838#issuecomment-823551891
final uri = Uri(
scheme: 'mailto',
query: queryParameters.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&'),
);
if (await urlLauncher.canLaunch(uri.toString())) {
await urlLauncher.launchUrl(uri.toString(), const LaunchOptions());
} else {
throw Exception('Unable to share on web');
}
}
}
/// Share files
@override
Future<void> shareFiles(
List<String> paths, {
List<String>? mimeTypes,
String? subject,
String? text,
Rect? sharePositionOrigin,
}) {
final files = <XFile>[];
for (var i = 0; i < paths.length; i++) {
files.add(XFile(paths[i], mimeType: mimeTypes?[i]));
}
return shareXFiles(
files,
subject: subject,
text: text,
sharePositionOrigin: sharePositionOrigin,
);
}
/// 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.
@override
Future<ShareResult> shareXFiles(
List<XFile> files, {
String? subject,
String? text,
Rect? sharePositionOrigin,
}) async {
// See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
final webFiles = <html.File>[];
for (final xFile in files) {
webFiles.add(await _fromXFile(xFile));
}
await _navigator.share({
if (subject?.isNotEmpty ?? false) 'title': subject,
if (text?.isNotEmpty ?? false) 'text': text,
if (webFiles.isNotEmpty) 'files': webFiles,
});
return _resultUnavailable;
}
static Future<html.File> _fromXFile(XFile file) async {
final bytes = await file.readAsBytes();
return html.File(
[ByteData.sublistView(bytes)],
file.name,
{
'type': file.mimeType ?? _mimeTypeForPath(file, bytes),
},
);
}
static String _mimeTypeForPath(XFile file, Uint8List bytes) {
return lookupMimeType(file.name, headerBytes: bytes) ??
'application/octet-stream';
}
}
const _resultUnavailable = ShareResult(
'dev.fluttercommunity.plus/share/unavailable',
ShareResultStatus.unavailable,
);