🔔

flutter_local_notificationでローカル通知を出す方法

5 min read

公式ドキュメントが英語で長いので、カスタムとかは置いておいてとりあえず試したい人向けの日本語ガイドとして書きます。
実装パートの例は各OSの (だいたい) ミニマルです。
iOS / Android 両方実機で動作確認済です。

準備

iOS

ありがちな info.plist の編集は不要ですが、Swiftのコードをイジる必要があります。
Flutterプロジェクトに自動生成される AppDelegate.swift というファイルに以下のコードを追加します。

if #available(iOS 10.0, *) {
  UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}

これがないと通知が出ません。
挿入する位置はだいたいこんな感じ。

ios/Runner/AppDelegate.swift
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
+   // flutter_local_notification
+   if #available(iOS 10.0, *) {
+     UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
+   }

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Android

特に設定しなくても通知自体は動きますが、バナーを出したい場合はfullScreenIntentに対応する必要がある[1]ので AndroidManifest.xml に以下のように追加します。

android/app/src/main/AndroidManifest.xml
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.myapp">
    <application
          android:label="myapp"
          android:icon="@mipmap/ic_launcher">
          <activity
              android:name=".MainActivity"
              android:launchMode="singleTop"
              android:theme="@style/LaunchTheme"
              android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
              android:hardwareAccelerated="true"
+             android:showWhenLocked="true"
+             android:turnScreenOn="true">
 

実装

iOS

ボタンか何かに書いて実行すると、通知権限の許可ダイアログが出て、即時でバナーが出ます。

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Future<void> notify() {
  final flnp = FlutterLocalNotificationsPlugin();
  return flnp.initialize(
    InitializationSettings(
      iOS: IOSInitializationSettings(),
    ),
  ).then((_) => flnp.show(0, 'title', 'body', NotificationDetails()));
}

initialize のタイミングで権限がリクエストされるので、アプリ起動時にリクエストしたいときは main とかでinitializeします。また、initialize 前に show を呼んでも権限がないので通知が出せません。
何度initializeしても問題なさそうなので、権限のリクエストが初めて通知するタイミングで良ければ、このnotify関数をいじってラッパーにしてしまっても便利だと思います。

Android

Androidは通知に使うアイコンを指定するためにinitializeが必要です。
また、通知チャネルの概念が存在するので、NotificationDetailsに設定を渡す必要があります。
iOSユーザのために軽く説明すると、通知チャネルは通知の種類を定義するもので、ユーザは設定からチャネルごとに通知をオンオフできます。例えばECアプリで「おすすめ商品」と「クーポン情報」でチャネルを分けておくと、クーポンだけ通知がほしいユーザはおすすめ商品の通知を選択的にオフにできます。

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Future<void> notify() {
  final flnp = FlutterLocalNotificationsPlugin();
  return flnp.initialize(
    InitializationSettings(
      android: AndroidInitializationSettings('@mipmap/ic_launcher'),
    ),
  ).then((_) => flnp.show(0, 'title', 'body', NotificationDetails(
    android: AndroidNotificationDetails(
      'channel_id',
      'channel_name',
      'channel_description',
    ),
  )));
}

AndroidInitializationSettings に渡している文字列が最初に触れた、通知に使うアイコンの指定箇所です。
android/app/src/main/res/drawable の中身をここで指定できるようなので、カスタムしたい場合はここに画像を格納して、拡張子なしのファイル名を文字列で渡すことになりそうです。
例えば、android/app/src/main/res/drawable/icon.png を使いたければ AndroidInitializationSettings('icon') という感じ。

バナーを出したい場合は重要度の設定が必要[1:1]です。

  flnp.show(0, 'title', 'body', NotificationDetails(
    android: AndroidNotificationDetails(
      'channel_id',
      'channel_name',
      'channel_description',
+     importance: Importance.high,
+     priority: Priority.high,
    ),
  ))

importance は Android 8.0 (API レベル 26) 以降、priority は Android 7.1 (API レベル 25) 以前のための設定のようです。

通知のスケジュール

通知をスケジュールする場合は show の代わりに zonedSchedule を使います。
スケジュールに使われる時刻はDart標準のDateTimeではなく、TZDateTime なので、インストールしてimportします。

  import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+ import 'package:timezone/timezone.dart' as tz;
flnp.zonedSchedule(
  0,
  'title',
  'body',
  tz.TZDateTime.now(tz.UTC).add(Duration(seconds: 3)),
  NotificationDetails(
    android: androidNotificationDetails,
    ios: iOSNotificationDetails,
  ),
  androidAllowWhileIdle: true,
  uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
)

iOSだけで使う場合も androidAllowWhileIdle は必須なので設定します。

検証環境

  • macOS 11.4 on x86
  • Flutter 2.2.3
  • Dart 2.13.4
  • Xcode 12.5.1
  • Android Studio 2020.3.1
  • テスト機
    • iPhone 11 Pro (iOS 14.7.1)
    • Pixel 3a (Android 11)
脚注
  1. バナーを出すための設定について Android Developers - ヘッドアップ通知 ↩︎ ↩︎

Discussion

ログインするとコメントできます