🔔

Flutterで毎朝8時に飛ぶローカル通知を実装する【OB-1アプデ】

2023/06/08に公開

以下の動画の要点だけをまとめたものを記事にしたいと思います。

動画ではあえて調べている過程なども載っけているので、むしろそこを見たい人は動画を見てください。

https://youtu.be/XCUqS1VA-Ps

基本的なローカル通知の実装

基本的には以下のpackageのReadmeに書いてあることです。

https://pub.dev/packages/flutter_local_notifications

Androidセットアップ

android/app/build.gradleに必要なものを追加します。

android/app/build.gradle
android {
  defaultConfig {
    multiDexEnabled true
  }

  compileOptions {
    // Flag to enable support for the new language APIs
    coreLibraryDesugaringEnabled true
    // Sets Java compatibility to Java 8
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

dependencies {
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
  implementation 'androidx.window:window:1.0.0'
  implementation 'androidx.window:window-java:1.0.0'
}

android/build.gradle にも必要なものを追加します。

android/build.gradle
buildscript {
   ...

    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.2'
        ...
    }

iOSセットアップ

AppDelegate.swift に以下を追加します。

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

package追加

以下のコマンドを叩いてpackageを追加します。

flutter pub add flutter_local_notifications

通知の許可をとる

iOSとAndroidのものをそのまま全部張っちゃいます。

厳密にいうと、設定は色々変えれますが、一旦動かすという意味ではほぼコピーでいいと思います。

  Future<void> _requestPermissions() async {
    if (Platform.isIOS || Platform.isMacOS) {
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              MacOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
          );
    } else if (Platform.isAndroid) {
      final AndroidFlutterLocalNotificationsPlugin? androidImplementation =
          flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>();
      await androidImplementation?.requestPermission();
    }
  }

スケジュール通知を作成

まず、tzを定義しときましょう。
timezoneのpackageはflutter_local_notificationsにDependenciesとして入っています。

import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

次に具体的なスケジュール通知。

  Future<void> _scheduleDaily8AMNotification() async {
    await flutterLocalNotificationsPlugin.zonedSchedule(
      0,
      'OB-1',
      '本日の顔を撮影をしましょう',
      _nextInstanceOf8AM(),
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'ob-1-face-daily',
          'ob-1-face-daily',
          channelDescription: 'Face photo notification',
        ),
        iOS: DarwinNotificationDetails(
          badgeNumber: 1,
        ),
      ),
      uiLocalNotificationDateInterpretation:
          UILocalNotificationDateInterpretation.absoluteTime,
      androidAllowWhileIdle: true,
    );
  }
  
  // 1回目に通知を飛ばす時間の作成
  tz.TZDateTime _nextInstanceOf8AM() {
    final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
    tz.TZDateTime scheduledDate =
        tz.TZDateTime(tz.local, now.year, now.month, now.day, 8);
    if (scheduledDate.isBefore(now)) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }
    return scheduledDate;
  }

毎日繰り返す設定

matchDateTimeComponents: DateTimeComponents.time が魔法の1行です。これを設定すると、設定した日付時刻には1回めに通知され、2回目以降は同じ時間に繰り返させることができます。

DateTimeComponentsは、dayOfWeekAndTime dayOfMonthAndTime dateAndTime があって、dayOfWeekAndTimeなら、2回目以降、同じ曜日の同じ時間に繰り返したりできます。

  Future<void> _scheduleDaily8AMNotification() async {
    await flutterLocalNotificationsPlugin.zonedSchedule(
      0,
      'OB-1',
      '本日の顔を撮影をしましょう',
      _nextInstanceOf8AM(),
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'ob-1-face-daily',
          'ob-1-face-daily',
          channelDescription: 'Face photo notification',
        ),
        iOS: DarwinNotificationDetails(
          badgeNumber: 1,
        ),
      ),
      uiLocalNotificationDateInterpretation:
          UILocalNotificationDateInterpretation.absoluteTime,
+     matchDateTimeComponents: DateTimeComponents.time,
      androidAllowWhileIdle: true,
    );
  }

通知をオフにする

通知を設定でオフにするための実装です。このへんは、OB-1の独自のやり方なので、気になる方は動画を見てみてください。

  • 処理の共通化
  • enumで安全なコードにする
  • shared_preferenceに保存

↑などを動画では解説しています。

追記

細かい部分で抜けている処理があったので追記です。

日本時間に修正

以下をmain.dartに書いておきましょう。これないと、ロンドン標準時刻になっちゃいました。

main.dart
  tz.initializeTimeZones();
  tz.setLocalLocation(tz.getLocation("Asia/Tokyo"));

バッジをリセット

https://pub.dev/packages/flutter_app_badger を入れて、以下のコードを叩くだけでバッジを消せます。

FlutterAppBadger.removeBadge();

まとめ

以上です。

↓アプリのコードはこちら(Flutter大学に入ってる人は見れます)
https://github.com/flutteruniv/ob-1

Flutter大学

Discussion