5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Flutter】シェア機能を実装する

Last updated at Posted at 2024-01-30

はじめに

シェア機能を実装する機会があり、調べましたので、記事にしたいと思います。
具体的な機能としては、Widgetを画像にして、シェアする機能です。
それを調べる過程で、テキストだけをシェアする方法もわかったので、それも含めて書いていきます。

シェアする方法

  • share_plusというパッケージを使用する
    (他にもパッケージはあるみたいですが、これがスタンダードで解説記事も1番多い)
  • URLスキームを使用する
     アプリを呼び出す用のURLをurl_launcherで呼び出します。
  • シェアするサービスが提供しているAPIを利用する
  • MethodChannelを使用してネイティブの機能を使用する
    share_plusもネイティブの機能を呼び出しているのですが、特定のアプリの場合、実装を変えるなどカスタマイズしたい場合はこちらの方法

share_plusを使う

OSの共有機能を利用するので、1番お手軽に、汎用性が高い共有機能を実装できます。
お手軽かつ、汎用性が高い分、特定のアプリに対してカスタマイズはできず、アプリによって、共有できる・できないの制限があります。
制限とは、共有する内容がテキストだけ、画像だけ、画像とテキストなど、共有内容によって動作が変わるアプリやそもそも共有先に選べないなどです。
例えば、画像とテキストの場合、以下のような制限がありました。

  • Instagramでは共有に失敗します(画像だけなら問題ないです)
  • Facebookには共有はできるがテキストが反映されない
  • Youtubeは選択対象として現れない(画像とテキストだから?動画なら選べるのかも)

このパッケージを使い、共有機能を実装する際は、最初に、何(画像だけ、テキストだけとか)を、どのアプリに共有するかを決めておくことをオススメします。

使用するパッケージ

  • share_plus
    OSのシェア機能を使うため使用
  • screenshot
    Widgetを画像にするため使用

テキストをシェア

テキストをシェアするのは簡単で、以下のように呼び出せばシェアできます。

import 'package:share_plus/share_plus.dart';

const text = "Shareします";
const subject = "Share先を選択";
Share.share(
  text,
  subject: subject,
);

textが共有先に送信する内容(Xならポストの本文)で、subjectが共有画面のタイトルになります。

IMG_6494.jpg

IMG_6495.PNG

共有先は、端末にインストールされたアプリに依存します。
XやLINEをインストールしていなければ、共有先には出てきません。

テキストの共有の場合は、InstagramやTikTokなどの動画や画像が必要なアプリは共有先には出てきません。

ちなみに、subjectはnullableなので、指定しなくても問題ないです。
その場合は、textがタイトルになります。

import 'package:share_plus/share_plus.dart';

const text = "Shareします";
Share.share(
  text,
);

IMG_6496.jpg

画像をシェア

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ScreenshotController _screenShotController = ScreenshotController();

  @override
  Widget build(BuildContext context) {
    return Screenshot(
      controller: _screenShotController,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text(widget.title),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              // Widgetをキャプチャする
              final screenshot = await _screenShotController.capture(
                delay: const Duration(milliseconds: 500),
              );
              if (screenshot != null) {
                final shareFile =
                    XFile.fromData(screenshot, mimeType: "image/png");
                await Share.shareXFiles(
                  [
                    shareFile,
                  ],
                );
              }
            },
            child: const Text("Share"),
          ),
        ),
      ),
    );
  }

  Future<void> _saveImage({
    required String imagePath,
    required Uint8List imageData,
  }) async {
    final imageFile = await File(imagePath).create();
    await imageFile.writeAsBytes(imageData);
  }
}

ポイント解説

Widgetを画像にする

以下の形で、childに指定したWidgetを画像にしています。

Screenshot(
  controller: _screenShotController,
  child: // 画像にするWidget,
)

実際にキャプチャするのは、下記の部分です。
delayを設定すると、設定した時間分キャプチャの時間をずらします。delayを設定しておかないと、即座にキャプチャされ、ボタンの押下のエフェクトが画像に表示されるので入れています。

// Widgetをキャプチャする
final screenshot = await _screenShotController.capture(
  delay: const Duration(milliseconds: 500));

画像をShareする

画像をShareするのは下記の部分です。

final shareFile = XFile.fromData(screenshot, mimeType: "image/png");
await Share.shareXFiles(
  [
    shareFile,
  ],
);

画像だけじゃなく、textも設定可能です。

final shareFile = XFile.fromData(screenshot, mimeType: "image/png");
await Share.shareXFiles(
  [
    shareFile,
  ],
  text: "画像をShare!",
);

IMG_6498.PNG

subjectを設定しても、下記のようにタイトルは変わらないみたいです。(androidは未確認)

IMG_6499.jpg

Instagramの場合、textを設定すると、シェアできないみたいです。
Instagramには、複数枚の写真はシェアできない仕様らしく、textを設定していると、複数枚シェアしているという判定になるみたいです。

IMG_6497.jpg

URLスキームを使用する

URLスキームを使用すると、特定のアプリケーションを起動して、特定のアクションを行うことができます。
例えば、Twitterを開き、Tweetしたい場合以下のようにします。

Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    // 追加
	<key>LSApplicationQueriesSchemes</key>
	<array>
  		<string>twitter</string>
	</array>
</dict>
</plist>
import "package:url_launcher/url_launcher.dart";

final url = Uri.parse("twitter://post?message=Hello");
final canOpen = await canLaunchUrl(url);
if (!canOpen) {
  print("Twitter is not installed");
  return;
}

await launchUrl(url);

シェアするサービスが提供しているAPIを利用する

サービスによっては、お金がかかったり、制限がありますが、シェア先に指定したいサービスのAPIを利用する方法です。
しかし、share_plusを使うURLスキームを使用する方法とは異なり、シェア先のアプリを起動して操作して共有するわけではないので、選択肢として考えることはあまりなさそうです。

MethodChannelを使用してネイティブの機能を使用する

ネイティブの機能を使用する方法で、下記の記事のように特定のアプリで共有したい内容などカスタマイズしたい場合はこちらの方法で実装する必要がありそうです。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?