はじめに
シェア機能を実装する機会があり、調べましたので、記事にしたいと思います。
具体的な機能としては、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が共有画面のタイトルになります。
共有先は、端末にインストールされたアプリに依存します。
XやLINEをインストールしていなければ、共有先には出てきません。
テキストの共有の場合は、InstagramやTikTokなどの動画や画像が必要なアプリは共有先には出てきません。
ちなみに、subject
はnullableなので、指定しなくても問題ないです。
その場合は、text
がタイトルになります。
import 'package:share_plus/share_plus.dart';
const text = "Shareします";
Share.share(
text,
);
画像をシェア
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!",
);
URLスキームを使用する
URLスキームを使用すると、特定のアプリケーションを起動して、特定のアクションを行うことができます。
例えば、Twitterを開き、Tweetしたい場合以下のようにします。
<?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を使用してネイティブの機能を使用する
ネイティブの機能を使用する方法で、下記の記事のように特定のアプリで共有したい内容などカスタマイズしたい場合はこちらの方法で実装する必要がありそうです。