めざまし電話というアプリを作ろうとしたとき通知機能を使った。このプラグインではローカルで定刻に通知を出すことができる。
アラームの後に逆モーニングコールする目覚ましアプリを作ってみた(かった)
GitHub: Stickers-hackathon/mezamashi-denwa
環境
Flutter 2.2.0
flutter_local_notifications 8.2.0
準備
インストール
$ flutter pub add flutter_local_notifications
として完了。
または、pubspec.yamlに
dependencies:
flutter_local_notifications: ^8.2.0
と追加して、pub getで完了。
権限委譲
プロジェクト名/android/app/src/main/AndroidManifest.xml に以下の行を追加する。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mezamashi_denwa">
<application
android:label="mezamashi_denwa"
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:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
#追加 再起動時およびアプリケーションの更新後も通知のスケジュールを確実に維持するために必要
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
</intent-filter>
</receiver>
#追加 プラグインがスケジュールされた通知の表示を処理するために必要
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
</application>
#追加 端末が起動されたときに通知を受ける権限を要求する
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
#追加 (省略可)Android通知のバイブレーションパターンをカスタマイズする場合に必要
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>
アイコン追加
通知にアイコン画像が必要なので、プロジェクト名/android/app/src/main/res/drawableに保存する。
例: https://github.com/Stickers-hackathon/mezamashi-denwa/tree/develop/android/app/src/main/res/drawable
インポート
使いたいソースファイルで
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart'; //以下3行は定刻通知に必要
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
コード内準備
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
String? selectedNotificationPayload;
Future<void> main() async {
// needed if you intend to initialize in the `main` function
WidgetsFlutterBinding.ensureInitialized();
await _configureLocalTimeZone();
const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS = IOSInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String? payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
selectedNotificationPayload = payload;
});
runApp(MyApp());
}
Future<void> _configureLocalTimeZone() async {
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
指定時刻に通知
以下はSwitchListTileをONにしたとき午前10時にスケジュールを設定するコード
Widget _buildTile(BuildContext context) {
return SwitchListTile(
title: Text("10:00 AM"),
subtitle: Text(""),
onChanged: (bool value) async {
var t = "10:00 AM";
final format = DateFormat.jm();
TimeOfDay schedule = TimeOfDay.fromDateTime(format.parse(t));
value ? _cancelNotification(0) : _zonedScheduleNotification(schedule, 0);
});
}
Future<void> _zonedScheduleNotification(TimeOfDay t, int i) async { // iは通知のID 同じ数字を使うと上書きされる
tz.TZDateTime _nextInstanceOfTime() {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, t.hour, t.minute);
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
return scheduledDate;
}
await flutterLocalNotificationsPlugin.zonedSchedule(
i,
'scheduled title',
'scheduled body',
_nextInstanceOfTime(),
const NotificationDetails(
android: AndroidNotificationDetails('your channel id',
'your channel name', 'your channel description')),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
}
Future<void> _cancelNotification(int i) async { //IDを指定して通知をキャンセル
await flutterLocalNotificationsPlugin.cancel(i);
}
参考
flutter_local_notifications 8.2.0
FlutterでローカルからNotification
Flutterでローカルな通知機能を実装するためのOSごとの前設定