【Flutter】FCMとSupabase Edge FunctionsでPush通知〜第2回〜
公開日2024.12.10
![【Flutter】FCMとSupabase Edge FunctionsでPush通知〜第2回〜](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/8f85a40be0f44cf68ed499c2f95c69c0/b1.png?fm=webp&fit=max&w=3840)
前回に続き、今回は、Push通知の受信までの実装を進めていきます。
改めて開発の流れの紹介
- Firebaseの設定
- Firebaseの環境分け(devとprod)
- FlutterでFirebase初期化を実装
- FCMの設定【← 第1回はここまで】
- Flutterで通知許可ダイアログを実装
- バックグラウンド・フォアグラウンド・ターミネート状態でのPush通知の受信
- Firebase Messagingでテスト送信【← 第2回はここまで】
- Supabaseインストール
- Supabase Edge Functionsの実装
- Webhookを設定
- SupabaseからFlutterアプリにPush通知を送信【← 第3回はここまで】
Flutterで通知許可ダイアログを実装
許可ダイアログを表示したい箇所に以下コードを記載します。
await ref.read(pushNotificationRepositoryProvider).requestPermission();
バックグラウンド・フォアグラウンド・ターミネートでのPush通知を実装
バックグラウンド・フォアグラウンド・ターミネート状態でのPush通知を受信する実装です。
フォアグラウンド・ターミネート通知に関しては、デフォルトのままだと通知を受信しても、通知バーに表示されないようになっています。
通知を表示するために、flutter_local_notifications
パッケージを使ってカスタム通知を表示する方法が一般的です。
android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
ios/Runner/AppDelegate.swift
import UIKit
import Flutter
import flutter_local_notifications
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 以下追加
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
GeneratedPluginRegistrant.register(with: registry)
}
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
// ここまで
main.dart
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
logger.i('Handling a background message: ${message.messageId}');
}
class _AppInitializer extends HookConsumerWidget {
const _AppInitializer({required this.child});
final Widget child;
@override
Widget build(BuildContext context, WidgetRef ref) {
...
Future<void> init() async {
ref.read(pushNotificationRepositoryProvider).initialize(
handler: _firebaseMessagingBackgroundHandler,
);
ref.read(pushNotificationRepositoryProvider).handle();
...
}
final pushNotificationRepositoryProvider =
Provider<PushNotificationRepository>((ref) {
return PushNotificationRepository(ref);
});
class PushNotificationRepository {
PushNotificationRepository(this._ref);
final Ref _ref;
final messaging = FirebaseMessaging.instance;
final onMessaging = FirebaseMessaging.onMessage;
final spabase = Supabase.instance.client;
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
final channel = const AndroidNotificationChannel(
'fcm_channel',
'Fcm Notifications',
description: 'This channel is used for Fcm notifications.',
importance: Importance.high,
);
void initialize({
required Future<void> Function(RemoteMessage) handler,
}) {
FirebaseMessaging.onBackgroundMessage(handler);
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
messaging.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
void handle({VoidCallback? callbackRouter}) {
// プッシュ通知を開いた時のハンドリング
void handleMessage(RemoteMessage message) {
final callback = callbackRouter;
if (callback != null) {
callback();
}
}
// Terminatedから開いた時
messaging.getInitialMessage();
// Backgroundから開いた時
FirebaseMessaging.onMessageOpenedApp.listen(handleMessage);
FirebaseMessaging.onMessage.listen((message) {
final notification = message.notification;
final android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '@mipmap/ic_stat',
),
),
);
}
});
}
Future<String?> getToken() async {
return messaging.getToken();
}
Future<void> requestPermission() async {
final settings = await messaging.requestPermission();
if (settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional) {
await setupFcm();
}
}
Future<void> setupFcm() async {
final fcmToken = await getToken();
if (fcmToken != null) {
logger.i('🐯 FCM TOKEN: $fcmToken');
await saveFcmToken(fcmToken);
}
messaging.onTokenRefresh.listen((newToken) async {
await saveFcmToken(newToken);
});
}
Future<void> saveFcmToken(String fcmToken) async {
// NOTE: FCM TokenをDBにセット(ご自身の環境に合わせて記載してください)
}
// 全体配信・Topicでsubscribe
void subscribeToTopic() {
messaging.subscribeToTopic('all');
}
}
これでPush通知を受信できるようになります。
Firebase Messagingでテスト送信して受信を確認
実際に、FirebaseからPush通知を送信してみます。
通知の作成から手動でテスト送信しましょう。
![Firebaseメッセージングのオンボーディング](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/79e96abcf50442e4ac5c538d8f787441/image%20(10).png?w=1428&fit=max&fm=webp)
![Firebaseメッセージングの通知の作成](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/c26d4a597f4948ee9b4170593e24971a/image%20(11).png?w=1428&fit=max&fm=webp)
![Firebaseメッセージングでテスト送信](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/28074522b18c4c799fbe6fbc87614111/image%20(12).png?w=1428&fit=max&fm=webp)
バックグラウンドでのPush通知の受信を確認
Androidはシミュレーター上で確認できます。
![AndroidでバックグラウンドでのPush通知の受信を確認](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/b125a9cd9ba64900aa70b349e7e3c144/1.png?w=1428&fit=max&fm=webp)
iOSは実機ビルドが必要です。
![iOSでバックグラウンドでのPush通知の受信を確認](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/0fa18aa8dfd74d0384e2f2c8d9913636/2.png?w=1428&fit=max&fm=webp)
フォアグラウンド・ターミネートでのPush通知の受信を確認
![フォアグラウンド・ターミネートでのPush通知の受信を確認](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/630496d388044a50a21b4d5a57756e91/3.png?w=1428&fit=max&fm=webp)
![フォアグラウンド・ターミネートでのPush通知の受信を確認](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/47ce5669ce984b0594d069d2b81e966e/5.PNG.png?w=1428&fit=max&fm=webp)
![フォアグラウンド・ターミネートでのPush通知の受信を確認](https://images.microcms-assets.io/assets/4458de1bcd404d52bbf31253395cc937/c96f397124034f57842b1639c3a479ab/4.png?w=1428&fit=max&fm=webp)
今回はこれで一旦完了です。
続きは以下の記事を見てください!