FlutterでFCMを使ったプッシュ通知の実装方法 (Android)

Firebase

FlutterでFCMを使ったプッシュ通知の実装方法 (Android) を紹介します!

プッシュ通知は、アプリにとって必須な機能だと思います。

今回は、Androidのみですが今後iOSでの実装も紹介する予定です。

プッシュ通知の受信実装

今回は、タイトル通りAndroidのみの実装方法紹介です。

ただ、iOSからAndroidにプッシュ通知を送信するところまで確認できました。

環境

$ flutter doctor
[✓] Flutter (Channel stable, 1.22.2, on Mac OS X 10.15.6 19G2021, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 12.0)
[✓] Android Studio (version 4.1)
[✓] VS Code (version 1.53.2)

今回作成するFlutterプロジェクトのAndroidはkotlinを使用しています。

Step1. Androidセットアップ

まずは、Firebaseでプロジェクト作成するとき、表示される通りの手順踏んでおきましょう。

その手順に追加して、プロジェクト/android/app/build.gradleCloud MessagingSDKを追加しましょう。

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.google.firebase:firebase-messaging-ktx:21.0.1'  //追加
}

最新バージョンはこちらから→Firebase SDK

作成したFlutterプロジェクトのAndroidはkotlinを使用しているので、ktxが付いたものを追加しました。

Step2. Android バックグラウンド設定

ここで、アプリがバックグラウンドでもメッセージを受信できるようにします。

プロジェクト/android/app/src/main/kotlin/~/配下にkotlinクラスを作成していきます。
MainActivity.ktがあるディレクトリです。

Stpe1.  FirebaseCloudMessagingPluginRegistrant.ktの作成
package <パッケージ名>

import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin

object FirebaseCloudMessagingPluginRegistrant {
    fun registerWith(registry: PluginRegistry?) {
        if (alreadyRegisteredWith(registry)) {
            return
        }
        FlutterFirebaseMessagingPlugin.registerWith(
                registry?.registrarFor(
                        "io.flutter.plugins.firebase.messaging.FirebaseMessagingPlugin"))
    }

    private fun alreadyRegisteredWith(registry: PluginRegistry?): Boolean {
        val key: String? = FirebaseCloudMessagingPluginRegistrant::class.java.canonicalName
        if (registry?.hasPlugin(key)!!) {
            return true
        }
        registry.registrarFor(key)
        return false
    }
}
Step2. Application.ktの作成
package com.example.skype_clone

import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService

class Application : FlutterApplication(), PluginRegistrantCallback {

  override fun onCreate() {
    super.onCreate()
    FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this)
  }

  override fun registerWith(registry: PluginRegistry?) {
    FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
  }
}
Step3. AndroidManifest.xmlの変更

作成した、FirebaseCloudMessagingPluginRegistrant.kt・Application.ktを読み込ませます。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="プロジェクト名">
    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:name=".Application"  ##編集
     android:label="プロジェクト名"
        android:icon="@mipmap/ic_launcher">

上記のように、<application android:name = “.Application” …> のように変更しましょう。

https://firebase.flutter.dev/docs/messaging/overview/
ここのサイトによると、FirebaseCloudMessagingPluginRegistrant.ktは要らなのかも、、

Step3. Flutterの設定

ここから、Flutterの設定をしていきます。

Step1. プラグインのインストール

firebase_coreのバージョンの兼ね合いでfirebase_messaging: ^8.0.0-dev.14をインストールしています。

pubspec.yamlに以下の追記します。

dependencies:
  flutter:
    sdk: flutter

  firebase_core: ^0.7.0
  firebase_messaging: ^8.0.0-dev.14
  flutter_local_notifications: ^4.0.1+1
Step2. main.dart

基本、ここのexampleに書かれているように実装すれば大丈夫です。
https://pub.dev/packages/firebase_messaging/versions/8.0.0-dev.14

全文

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
  print("Handling a background message ${message.messageId}");
}

/// Create a [AndroidNotificationChannel] for heads up notifications
const AndroidNotificationChannel channel = AndroidNotificationChannel(
  'high_importance_channel', // id
  'High Importance Notifications', // title
  'This channel is used for important notifications.', // description
  importance: Importance.high,
  enableVibration: true,
  playSound: true,
);

/// Initalize the [FlutterLocalNotificationsPlugin] package.
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Set the background messaging handler early on, as a named top-level function
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);

  await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
    alert: true,
    badge: true,
    sound: true,
  );

  await DotEnv.load(fileName: '.env');

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  AuthMethods _authMethods = AuthMethods();

  @override
  void initState() {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification notification = message.notification;
      AndroidNotification android = message.notification?.android;

      if (notification != null && android != null) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              channel.description,
              // TODO add a proper drawable resource to android, for now using
              //      one that already exists in example app.
              icon: 'launch_background',
            ),
          ),
        );
      }
    });

    String token = await FirebaseMessaging.instance.getToken();
    print('token: $token');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Push Notification',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PushNotification(),
    );
  }
}

class PushNotification extends StatefulWidget {
  @override
  _PushNotificationState createState() => _PushNotificationState();
}

class _PushNotificationState extends State<PushNotification> {
  @override
  void initState() {
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage message) async {
      print(message);
    });

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
      print('A new onMessageOpenedApp event was published!');
      print(message);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('push notification'),
      ),
      body: Center(
        child: Container(
          child: Text('プッシュ通知'),
        ),
      ),
    );
  }
}
アプリ起動
$ flutter run

flutter アプリ起動

起動できれば一旦flutterの設定は完了です。

Step4. Firebase Cloud Messagingで通知作成

Cloud Messagingで通知作成準備

FirebaseのCloud MessagingからSend your first messageを選択して通知を作成しましょう。

cloud messaging 画面

通知作成

タイトルとメッセージ内容を設定したら テスト メーメッセージを送信を選択をします。

プッシュ通知 作成 画面

FCMトークン登録

FCM 登録トークン追加にデバイストークンを登録しましょう。
このトークンはメッセージ送信の宛先になります。

取得コードはこちら!

String token = await FirebaseMessaging.instance.getToken();

FCM トークン登録 画面

追加したら右下のテストを押して送信して届くか見てみましょう。

通知受信

プッシュ通知 取得 画面

画像のように通知が表示されれば完了です!

バックグラウンドで通知受信

アプリがバックグランドでも通知を受信をできるか見みましょう。

先程同様に通知を送信しましょう。

プッシュ通知 取得 画面

画像のように通知が表示されれば完了です!

通知をタップして任意の画面を開く

通知をタップして任意の画面(ex. メッセージ表示画面)に遷移するようにしてましょう。

下記のコードのようにmain.dartを修正

...

class PushNotification extends StatefulWidget {
  @override
  _PushNotificationState createState() => _PushNotificationState();
}

class _PushNotificationState extends State<PushNotification> {
  @override
  void initState() {
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage message) async {
      if (message != null) {
        print(message);
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => MessagePage(message: message),
          ),
        );
      }
    });

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
      print('A new onMessageOpenedApp event was published!');
      print(message);
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MessagePage(message: message),
        ),
      );
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('push notification'),
      ),
      body: Center(
        child: Container(
          child: Text('プッシュ通知'),
        ),
      ),
    );
  }
}

class MessagePage extends StatelessWidget {
  final RemoteMessage message;

  MessagePage({this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('tap notification'),
      ),
      body: Center(
        child: Container(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('title: ${message.notification.title}'),
              Text('body: ${message.notification.body}'),
            ],
          ),
        ),
      ),
    );
  }
}

修正できたら起動しましょう。

下記のような動きになれば完了です!

プッシュ通知の送信方法

送信方法はそんなに難しくなく、fcm サーバー?POSTリクエストを送ればOKです。

コードはこちら

import 'package:http/http.dart' as http;

sendNotification(){
  try {
      http.post(
        'https://fcm.googleapis.com/fcm/send',
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'key=$serverKey',
        },
        body: jsonEncode({
          'to': $recieverToken,
          'priority': 'high',
          'notification': {
            'title': 'push',
            'body': 'ぷっしゅ通知',
          }
        }),
      );
    } catch (e) {
      print(e);
    }
}

$serverKey にはプロジェクトのサーバーキー、
$receiverToken には受信者となるデバイスのトークンを設定してください。

サーバーキーはプロジェクト設定のCloud Messagingから確認できます。

firebase serverkey

これで、sendNotification()を実行すれば送信れます!

おわり

以上でAndroidでのプッシュ通知の受信は完了です。
お疲れさまでした!

Flutterでプッシュ通知を実装しようとして調べてもプラグイン等がアップデートしているものが多く、古めの情報になってしまいとても苦労しました。(;´Д`)

2021年から実装される方々にとって役に立ってくれればとても嬉しいです!

コメント

タイトルとURLをコピーしました