Flutter + Firebaseで、Android端末にプッシュ通知を送るための初めの一歩

#はじめに

本記事ではFlutterとFirebaseを使い、「プッシュ通知を送るとにかくシンプルな仕組みを作りたい！」という人向けの手順を紹介しています。
なるべく丁寧に記述しているため、FlutterとFirebaseを初めて連動させてみたい方、プッシュ通知の仕組みを学んでみたい方に参考になるかと思います。

#前提

* Firebaseプロジェクトを作成済みで、利用可能な状態である。
* FlutterとAndroid Emulatorを導入しており、Flutterのデモアプリが立ち上げられる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/7f989412-7534-775e-63d9-4cccd053dfe0.png)←こんなやつね




#アプリで通知を受け取る準備
Firebaseの機能であるCloud MessagingやCloud Functions for Firebaseで作成した通知を受け取るには、FirebaseのマイアプリへFlutterのアプリの登録が必要です。

まずはFirebaseのマイアプリにFlutterのデモアプリを登録してやりましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/d6ec84cc-4e43-1461-251b-c099aca43e69.png)
Firebaseにアクセスし、歯車マークから「プロジェクトを設定」に移動します。

「マイアプリ」の「アプリを追加」をクリックし、アプリ追加画面に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/7dac689c-db0b-eeeb-64ae-4933c774afb5.png)
「Androidパッケージ名」には`{プロジェクトのディレクトリ}/app/build.gradle`の`applicationId`の値を入力してあげましょう。
アプリのニックネームなどは適当でOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/da9299f8-7537-d979-eb07-4678462e254f.png)

設定時にgoogle-services.jsonがダウンロードできるのでダウンロードして、`{プロジェクトのディレクトリ}/android/app`のディレクトリに置いてあげましょう。
また、同じディレクトリにあるbuild.gradleの24行目周辺に下記を記述します。

```build.gradle
...

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'  // 追加した行

...

dependencies {
    implementation 'com.google.firebase:firebase-analytics:17.2.1'  // 追加した行
    implementation 'com.google.firebase:firebase-messaging:20.1.7'  // 追加した行

...

```

次に、アプリで通知を受け取る準備をしてやりましょう。
下記記事を参考に、`{プロジェクトのディレクトリ}/android/app/src/main/kotlin/com/~~~/~~~/java`にMyFirebaseMessagingService.javaを作成してやります。

AndroidアプリにFirebase Cloud Messagingを実装する(2) - アプリでメッセージを受信する
https://qiita.com/outerlet/items/49a5346ba206eaf877d5

```MyFirebaseMessagingService.java
package com.~~~.~~~.java;  // ~~~の部分は自分のパッケージ構成に合わせてやる

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    @Override
    public void onNewToken(String token) {
        // 端末＋アプリを一意に識別するためのトークンを取得
        //Log.i("FIREBASE", "[SERVICE] Token = " + token);
    }


    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        if (remoteMessage != null) {
            // 通知メッセージ
            RemoteMessage.Notification notification = remoteMessage.getNotification();
            if (notification != null) {
                // 通知メッセージを処理
                System.out.println("MESSAGE : " + remoteMessage.getNotification().getBody());
            }

            // データメッセージ
            /*
            Map<String, String> data = remoteMessage.getData();
            if (data != null) {
                // データメッセージを処理
            }
            */
        }
    }
}
```

次に、`{プロジェクトのディレクトリ}/android/app/src/main/AndroidManifest.xml`を編集してやります。

```AndroidManifest.xml
...
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

        <!-- 追加する行 ここから -->
        <service
            android:name=".java.MyFirebaseMessagingService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
        <!-- 追加する行 ここまで -->

    </application>
</manifest>
```

これでアプリ側で通知を受け取る準備ができました。

次に、`{プロジェクトのディレクトリ}/pubspec.yaml`に`firebase_messaging: ^6.0.16`を記述し、アプリ内でfirebase関連のメソッドを使えるようにしてやりましょう。

```pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_messaging: ^6.0.16  // 追加した行
```

これでCloud Functions for Firebaseでメッセージを送信する際に必要なトークンを取得する準備ができました。

ここでトークンを取得するのですが、今回はとりあえず動かしてみるのが目的のため、取得方法は適当でOKとします。
なので、デモアプリの中でコンソールに出力させる形で取得することにします。

`{プロジェクトのディレクトリ}/android/app/main.dart`の中の`_incrementCounter()`メソッドにトークン取得の処理をくっつけてやりましょう。

```main.dart
...
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
      getToken();  // 追加した行
    });
  }

  Future<void> getToken() async {                        // 追加した行
    String _token = await _firebaseMessaging.getToken(); // 追加した行
    print(_token );                                      // 追加した行
  }                                                      // 追加した行
 ...
```


これでデモアプリの+ボタン押下時にコンソールにトークンが出力されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/7a32b621-08fe-b724-1f45-07c3a50f6ccc.png)

ここで豆知識なのですが、このトークンはアプリの初回起動時に作成されるもので、同じデバイスであってもアプリの再インストール時などに作り変えられます。

# メッセージ送信方法その1：Cloud Messagingで通知を送る
Firebase側でCloud Messagingを使ってアプリに通知を送ります。
なお、この方法では上記で作成したトークンは不要となります。

Firebaseのメニューから「Cloud Messaging」をクリックし、通知の作成画面に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/83ef7437-6fcd-656e-ab78-0f435a8dfa27.png)

テストメッセージを適当に作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/52d885aa-d018-78f1-d4b3-260a3ca9f518.png)
テストメッセージを送るアプリを選択します。

スケジュール設定はデフォルトで「今すぐ送信」になっているので、そのまま確認→公開とクリックしましょう。
この時、Android Emulatorを立ち上げた状態にしておくことを忘れずに。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/8ebb43e4-4e63-1280-4828-96cff484a0e6.png)

エミュレータから通知音が鳴り、通知が届いていたら完了です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701737/a7f48cbb-738c-515f-ab74-bc62f6e7c818.png)


# メッセージ送信方法その2：Cloud Functionで通知を送る
Cloud Functionのnodejsのサポートバージョンが変わった影響で確認が取れなかったため、本記事での詳しい解説は割愛させていただきます……。

公式ドキュメントを参考にFirebaseのデプロイ環境を作成し、「Cloud Firestoreの対象テーブルのレコードが更新されるのをトリガーとして、対象のトークンを持つデバイスに通知を送る」などの仕組みから作り始めると楽そうです。
本記事内で作成したトークンを利用し、トークンとユーザを紐付け、バックエンド側で条件分けして通知対象ユーザを絞るような実用的なロジックが実装可能です。

実装にあたっては下記の記事などが参考になるかと思います。

Firebase 公式ドキュメント
https://firebase.google.com/docs/functions/get-started?hl=ja

Cloud FunctionsからPush通知を送信する
https://qiita.com/superman9387/items/ee32b2ccc0354d90c717

Cloud Functionsでモバイルにリッチプッシュ通知を送信する方法
https://tech.playground.style/firebase/push-notification/


#おわりに

本記事ではFlutter初心者向けに、通知を送受信出来るようにするための過程をなるべく優しく説明しました。
不正確な記述や、より良い方法がありましたらご指摘頂けると嬉しいです。