くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Flutter Webでもいい感じにシェアできるようにする(share_plus)

Flutterでシェア機能を利用したい場合、share_plusパッケージがあるけど、
Flutter Webの場合は、Web Share APIを使っていて、
Web Share APIをサポートしていないブラウザの場合、メーラーが立ち上がってしまう。。

なんとかいい感じにできないかと思い、
いろいろ調べてみたときの備忘録(*´ω`*)

できたもの

Web Share APIが使えるとき(Android/Chromeなど)は、 そのまま使い、

使えないとき(Mac/Chromeなど)は、
ダイアログを開く感じ。

こちらのアドカレで作ったやつで使ってます(*´ω`*)

メーラーが立ち上がる原因

該当の箇所はここっぽい。

dart:htmlNavigator.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などではサポートされていないぽい。。

やったこと

share_plusでは、mailtoになるってしまうので、
dart:htmlNavigator.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のときでもシェア機能がいい感じにできるように(*´ω`*)

参考にしたサイトさま