pco2699’s blog

学んだコード・技術について、保存しておく場所

flutterとFirebase Cloud Messagingを連携させて通知専用アプリをサクッとつくる

こんにちは、この記事はflutter advent calendar 2018 9日目の記事です。

TL;DR

  • 全然ネイティブアプリ作ったこと無いわしが、flutterでサクッと通知を受け取れるアプリを作った
  • クオリティや保守性はさておき通知の実装できたので実装のポイントなど
  • flutter いいよおいいよお

なぜつくることになったか

現在、私はcall.jpというサービスの立ち上げ、運営を手伝っています。 call.jpはWebページから、チューターや店員さんを呼べる呼び出しサービスです。

現在、ジーズアカデミーTOKYOなどで試験運用中です。

gsacademy.tokyo

以下の通り、通知受取がPCにしかありませんでした。

f:id:pco2699:20181209171558j:plain
call.jpの概要図

なので、サクッと通知を受け取れるアプリを作ることになりました。

アプリの要件

要件は以下の通りです。

  • とにかくシンプルに通知受け取れればOK
  • アプリ自体はバックグラウンドで動かす前提
  • iOS/Android両方ともサクッと対応できるとよい(チューターはiOS/Androidばらばらのため)

上記の要件を検討するにあたって、React Nativeなど検討しましたが以下からflutterがいいのでは、という話になりました。

  • flutterがGoogle製なので、同じくGoogleで現在利用しているfirebaseと相性がよい
  • 通知機能も問題なく両プラットフォームで実装できる
  • Dartもそんなにとっつきにくくなさそう(JavaとかJavascriptとかSwitftやってればできそう)
  • なんか最近、Twitter界隈でもよく見かけてイケてる感じがするw

なので、とりあえずflutterで簡単な通知アプリを実装してみることにしました。

つくったもの

名前の通り「通知を受け取るだけのアプリ」です。 ↓が実際につくったものです。

Firebase Cloud Messagingで通知を受け取る際のポイント

Firebase Cloud Messaingでflutterで通知を受け取る際のポイントは以下です。(すげぇ簡単です。)

  1. FirebaseMessagingのライブラリのインポート&アプリでFirebaseの設定
  2. アプリのログイン時、起動時など任意のタイミングで通知許可の設定を行う(iOSのみ)
  3. 通知受信時の挙動を定義する。
  4. トピックのサブスクライブを行う。

FirebaseMessagingのライブラリのインポート&アプリでFirebaseの設定

なにわともはれ、Firebase Messagingのライブラリのインポート&アプリでFirebaseの設定を行います。 Firebaseの設定は、Firebase公式のとこから各OSに合わせて、それに従います。 flutterの場合、プラットフォームに合わせて、各フォルダ下(ios, android)でfirebaseの設定を行います。

ライブラリのインポートはpubspec.yamlに以下を追記します。

// pubspec.yaml
dependencies:
  firebase_messaging: ^2.0.0 // ver番号は現在の状況に合わせて変えてください

アプリのログイン時、起動時など任意のタイミングで通知許可の設定を行う

ここからdartファイルに手を入れていきます。 自分が通知設定を行いたい画面のStateのinitState()に通知許可の設定を入れます。

自分は、立ち上げ時に通知許可の設定を出したかったので、HomeScreenに入れてます。

// home_screen.dart
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';

// HomeScreenの宣言
class HomeScreen extends StatefulWidget {
  @override
  State createState() => new HomeScreenState();
}

// HomeScreenのStateの宣言
class HomeScreenState extends State<HomeScreen> {
  final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
 @override
  void initState(){
    super.initState();
    // ここで通知許可の設定を行う
    _firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound: true, badge: true, alert: true));
    _firebaseMessaging.onIosSettingsRegistered
        .listen((IosNotificationSettings settings) {
      print("Settings registered: $settings");
    });
 }

通知受信時の挙動を定義する

同様にinitState()の中で、通知受信時の挙動を定義します。 私の場合は、ダイアログが出ればよいので、buildDialog()という関数を定義して ダイアログを出すようにしています。

// home_screen.dart
  // ダイアログを出す関数の設定
  void _buildDialog(BuildContext context, String message) {
    showDialog(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return new AlertDialog(
            content: new Text("$message"),
            actions: <Widget>[
              new FlatButton(
                child: const Text('CLOSE'),
                onPressed: () {
                  Navigator.pop(context, false);
                },
              ),
            ],
          );
        }
    );
 @override
  void initState(){
    super.initState();
    // ここで通知許可の設定を行う
    _firebaseMessaging.requestNotificationPermissions(
        const IosNotificationSettings(sound: true, badge: true, alert: true));
    _firebaseMessaging.onIosSettingsRegistered
        .listen((IosNotificationSettings settings) {
      print("Settings registered: $settings");
    });
    // ここで通知受信時の挙動を設定しています。
    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        _buildDialog(context, "onMessage");
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        _buildDialog(context, "onLaunch");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        _buildDialog(context, "onResume");
      },
    );
 }

ちなみに、onMessage, onLaunch, onResumeってなんぞやと思っていると思いますので調べてみました。 flutterのfirebase_messagingライブラリのREADMEにばっちし載っています。

pub.dartlang.org

以下私の日本語訳

アプリ - フォアグラウンド アプリ - バックグラウンド アプリ - 停止中
Notification on Android onMessage システムトレイに通知が格納される、もし通知をおしてアプリを開くとonResumeが呼ばれる(click_action: FLUTTER_NOTIFICATION_CLICKの設定が必要) システムトレイに通知が格納される、もし通知をおしてアプリを開くとonLaunchが呼ばれる(click_action: FLUTTER_NOTIFICATION_CLICKの設定が必要)
Notification on iOS onMessage システムトレイに通知が格納される、もし通知をおしてアプリを開くとonResumeが呼ばれる システムトレイに通知が格納される、もし通知をおしてアプリを開くとonLaunchが呼ばれる
Data Message on Android onMessage バックグラウンドにいる間はonMessageが呼ばれる プラグインではサポートされていない。メッセージはロストする。
Data Message on iOS onMessage メッセージはFCMによって保存されフォアグラウンドに戻ったときにonMessageが呼ばれる メッセージはFCMによって保存されフォアグラウンドに戻ったときにonMessageが呼ばれる

まさかのDataMessage on Androidでアプリが停止中だとメッセージがロストする、という。。。(自分も今、初めて気づいた...)

トピックをサブスクライブ

最後にトピックをサブスクライブします。 今回は、ボタンを押してサブスクライブしています。

  void _onChanged1() {
    _firebaseMessaging.subscribeToTopic("/topics/gsacademy");
    _buildDialog(context, '通知の受信を開始します');
  }

ちなみに、トピック以外にも端末者IDや通知トークンでも通知は受信可能です。

さいごに

flutterで動かしてますが、今の所、機能がシンプルなゆえ、基本問題なく、動いています。いやあflutter便利。

Firebase Cloud Messaging周りについては、以下のfirebase user group user meetupの資料にて詳しく扱っていますのでよろしければ参考にしてみてください。

speakerdeck.com