Flutterでシェア機能を利用したい場合、share_plusパッケージがあるけど、
Flutter Webの場合は、Web Share APIを使っていて、
Web Share APIをサポートしていないブラウザの場合、メーラーが立ち上がってしまう。。
なんとかいい感じにできないかと思い、
いろいろ調べてみたときの備忘録(*´ω`*)
できたもの
Web Share APIが使えるとき(Android/Chromeなど)は、 そのまま使い、
使えないとき(Mac/Chromeなど)は、
ダイアログを開く感じ。
こちらのアドカレで作ったやつで使ってます(*´ω`*)
メーラーが立ち上がる原因
該当の箇所はここっぽい。
dart:html
でNavigator.share()を呼んでいるが、
Web Share APIがなく、NoSuchMethodError
をcatchしたときは、
mailto
を利用している感じっぽい。。
import 'dart:html' as html; class SharePlusWebPlugin extends SharePlatform { final html.Navigator _navigator; @override Future<void> share(String text, { String? subject, Rect? sharePositionOrigin }) async { try { await _navigator.share({'title': subject, 'text': text}); } on NoSuchMethodError catch (_) { // ...略 // 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('&'), ); // ...略 } } }
dart:htmlにはcanShare()がない
Web Share APIではNavigator.canShare()
が用意されているが、
dart:html
側にはなく、Firefoxなどではサポートされていないぽい。。
- Navigator.canShare() - Web API | MDN
- Navigator class - dart:html library - Dart API
- Web Share API | Can I use... Support tables for HTML5, CSS3, etc
やったこと
share_plusでは、mailto
になるってしまうので、
dart:html
でNavigator.share()
を直接利用する感じにしてみた。
import 'package:share_plus/share_plus.dart'; // dart:htmlがあるプラットフォームのときだけ、importを切り替える import './share_interface.dart' if (dart.library.html) './share_interface_web.dart'; doShare(BuildContext context, {required String content}) async { if (kIsWeb) { // webの場合は、とりあえず、Navigator.share()を試す try { final shareInstance = getShareInstance(); await shareInstance.share(content: content); } on NoSuchMethodError catch (_) { if (!context.mounted) return; // Web Share APIが存在しない場合にダイアログを開く final dialog = ShareDialog(); dialog.showDialog( context: context, builder: (context) => ShareDialog(content: content), ); } } else { // web以外の場合は、share_plusを使う await Share.share(content); } }
dart:html
を使う部分は直接使うと別のプラットフォームで困るので、
Conditional Importを使って、importを切り替えるようにしている。
スタブ用のファイルは、とりあえず定義だけ。
// share_interface.dart // スタブ用のファイル class ShareInterface { share({required String content}) => throw UnsupportedError('not supported'); } ShareInterface getShareInstance() => ShareInterface();
Web用のファイルでは、Navigator.share()
を利用する。
// share_interface_web.dart // Web用のファイル // ignore: avoid_web_libraries_in_flutter import 'dart:html' as html; import './share_interface.dart'; class ShareInterfaceWeb implements ShareInterface { @override share({required String content}) async { final html.Navigator navigator = html.window.navigator; await navigator.share({'text': content}); } } ShareInterface getShareInstance() => ShareInterfaceWeb();
以上!! これでWebのときでもシェア機能がいい感じにできるように(*´ω`*)