FlutterでFirebase Cloud Messaging

プレーンなアプリに、Firebase Cloud Messagingのメッセージを受信できるまでの組込手順をつらつらと書くだけ。

https://pub.dev/packages/firebase_messaging の6.0.16 (2020/06/21時点) に記述された手順を自分なりに解釈・修正した手順なので、間違っているかもしれないのでご注意ください。

# 環境
- Flutter v1.17.3
- Android Studio 4.0
- Xcode 11.5

```
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.17.3, on Mac OS X 10.15.4 19E287, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
[✓] Android Studio (version 4.0)
```


# 手順

## pubspec.yaml更新

firebase_messagingを追加

## Flutter側実装

MyAppをStatefulWidgetにして以下のような実装をする。

```dart
  @override
  void initState() {
    super.initState();
    _initFirebaseMessaging();
  }

  Future<void> _initFirebaseMessaging() async {
    _firebaseMessaging = FirebaseMessaging();
    if (Platform.isIOS) {
      bool permitted =
          await _firebaseMessaging.requestNotificationPermissions();
      if (!permitted) {
        return;
      }
    }
    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print('FirebaseMessaging onMessage');
      },
      onLaunch: (Map<String, dynamic> message) async {
        print('FirebaseMessaging onLaunch');
      },
      onResume: (Map<String, dynamic> message) async {
        print('FirebaseMessaging onResume');
      },
    );
    String token = await _firebaseMessaging.getToken();
    print('FirebaseMessaging token: $token');
  }
```

## Android側修正

### google-services.json を置く

`android/app/google-services.json` に置く

### AndroidManifest.xml の修正

`android/app/src/main/AndroidManifest.xml` を編集、`android:name=".MainActivity"` のactivityに以下を追加

```xml
  <intent-filter>
      <action android:name="FLUTTER_NOTIFICATION_CLICK" />
      <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
```

### build.gradleの修正

#### android/build.gradle

buildScript -> dependencies に以下を追加

```
classpath 'com.google.gms:google-services:4.3.3'
```

#### android/app/build.gradle

以下を追加。

```
apply plugin: 'com.google.gms.google-services'
```

#### android/app/build.gradle の修正について(※7/1追記)

以下のドキュメントでは、`apply plugin`の追記は**ファイルの末尾に**という説明がある
- https://pub.dev/packages/firebase_messaging
- https://firebase.google.com/docs/android/setup の日本語のドキュメント

だが https://firebase.google.com/docs/android/setup の英語のドキュメントではその記述がなかった。
以下のように他のpluginを追加しているところで追記したが問題なく動いた。

```gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 28
    ....
}
```

**7/1追記**
私の環境では上記の通り、ファイルの末尾ではなくてもビルドが行え、またアプリにはPush通知が届くのを確認できた。が一方、うまく動かなかったという報告もあった。
なので、ファイルの末尾に記述するのが安全なのかもしれない。


## iOS側修正

### 証明書やProvisioning Profileの作成

多分これが一番めんどくさい。ひとまず以下を作成。

- iOS App Development
- Apple Push Notification service SSL (Sandbox)

ついでにApple Push Notifications serviceのAPNs Authentication Keyも作成して、Firebase Consoleで設定しておく。

### GoogleService-Info.plist を置く

Xcodeで開いてRunnerの下に置く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375661/307188fb-d515-cb28-20fc-91db2c4f135e.png)

### Signing & Capabilities の編集

先で手順した証明書およびProvisioning Profileを設定する。

加えて以下のCapabilityを追加する。

- Background Modes
- Push Notifications

Background Modesでは以下にチェックを入れておく。

- Background fetch
- Remote notifications

### AppDelegate.swiftの修正

以下を追加する。

```swift
if #available(iOS 10.0, *) {
  UNUserNotificationCenter.current().delegate = self
}
```

# 動作確認

Firebase Consoleで送って確認して受け取れるのを確認。
この実装だと、アプリがフォアグラウンドにいると通知が表示されないので、バックグラウンドに遷移させて動作確認する。