はじめに
Push通知ではなくアプリケーション上から通知を出すためにどうするのか調べました。
環境
Android Studio
Flutter1.0
今回はiOSを表示させる環境用意しなかったので、Androidのみに言及しています。
iOSだと動かない箇所が出てくると思います。
Dependencyの追加
Flutterには通知を簡単に実現するためのflutter_local_notifications
が用意されているので、それをpubspec.yaml
に追加してあげます。
バージョンがまだ0.5.1なので、バージョンアップ後に動かなくなる可能性がありますので、この先読む方はご注意ください。
dependencies:
flutter_local_notifications: ^0.5.1
アプリ側のトリガーを元に通知を行う
アプリ側のイベントを契機に通知を行う方法について見ていきます。
まずは必要となるpackageをimportしてあげます。
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
次からはState<T>
内部に実装する必要があるため、必要な実装は先に行なっておきます。
私はFlutter
を勉強しながら継ぎ足し製法でソースを拡充させているため、それを使います。
なお、色んなコードが入ってきてしまっているため、本ページでは全体のコードはのせませんがご了承ください。
State<T>
の実装が完了したらメンバ変数としてFlutterLocalNotificationsPlugin
を追加します。
様々なサンプルだとメンバ変数になってませんが、これを使って実際の通知を行う必要があるため、メンバ変数としないと無理です。
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
NotificationDetails platformChannelSpecifics;
まずは通知を行うための初期化処理を行います。
初期化はState<T>
のinitState
内に実装します。
ここでの注意点はサンプルをコピーしただけではエラーとなるため、実装が必要になる部分があることです。
また、実際の通知する際に使用するオブジェクトもここで生成しておいた方が良いです。
AndroidNotificationDetails
のコンストラクタはAndroid8.0以上のチャンネル機能用の通知用があるので、8.0以上を目的で作る場合は必須となります。
@override
void initState() {
super.initState();
_refresh();
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid = AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocationLocation);
var initializationSettings = InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.Max, priority: Priority.High);
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
}
そして必要な箇所を追加します。
onDidReceiveLocationLocation
はiOS用のようなので、本ページに必要ない実装ですがなぜか実装してあったので、合わせてのせておきます。
onSelectNotification
についてが今回重要な点となります。
このメソッドは通知をクリックした際に呼ばれるイベントとなっているため、その動作について実装が必要となります。
今回はNavigator.push
を使用して別のWidget
を呼び出してます。
そもそもNotificationからの起動時は今の画面の次の画面として起動するか、一から起動してから次の画面として起動するかという動作をする感じなので、Future.sync
などからWidget
を作っても現状では動作がどう変わるかが良くわかりませんでした。
何かパラメータを渡す際は引数のpayload
を使って行う必要があります。
ここはString
固定なので、複雑なデータを渡す際はJson
なんかで渡す必要があります。
Future onSelectNotification(String payload) async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Text(payload),
maintainState : false,),
);
}
Future onDidReceiveLocationLocation(
int id, String title, String body, String payload) async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: Text(body),
actions: <Widget>[
FlatButton(
child: Text(payload),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
}
);
}
最後に実際に通知を行う箇所です。
initState
で生成したオブジェクトを使用して通知します。
onSelectNotification
に渡ってくるpayload
はpush
のpayload
で設定したものと同じものなので、通知後にパラメータを渡したい場合はここで設定してあげてください。
Future _onNotification() async {
await flutterLocalNotificationsPlugin.show(
0, 'plain title', 'plain body', platformChannelSpecifics,
payload: 'item id 2');
}
これで通知できるようになりました。
通知をスケジューリングする
アプリケーショントリガーですぐに実行することが出来るようになりましたが、そのトリガーを元にすぐに出さずにあとで出すようにしたい場合にスケジューリングさせることが出来ます。
その場合にどうするべきかを見ていきます。
AndroidManifest.xmlの修正
スケジューリングされた通知を行うのにAlarmManager
使用しているらしく、それが使用できるようにManifestを修正してあげる必要があります。
修正内容は公式書いてあるので、その通りに追加してあげます。
AlermManager
を使用している場合はアプリが起動していなくても起動してくれるので、停止状態でも出せるようになります。
なお、com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver
を追加するとAndroid Studio上でエラーになりますが、特に問題ないので無視してください。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
dartファイル
コード的にはschedule
メソッドを使用して、時間を指定あげるだけの違いとなります。
Future _onNotification() {
return flutterLocalNotificationsPlugin.schedule(
0, 'plain title', 'plain body', DateTime.now().add(Duration(seconds: 5)),
platformChannelSpecifics, payload: 'item id 2');
}
繰り返し通知
Manifestの修正はしておいてください。
コード的にはperiodicallyShow
を使えば実現できます。
指定するRepeatInterval
はenumなので、簡単に繰り返す期間の指定が可能です。
Future _onNotification() {
return flutterLocalNotificationsPlugin.periodicallyShow(
0, 'plain title', 'plain body', RepeatInterval.EveryMinute,
platformChannelSpecifics, payload: 'item id 2');
}
特定の時間で繰り返す
毎日定時に起動させたい場合はshowDailyAtTime
を使用します。
Future _onNotification() {
var date = DateTime.now().add(Duration(seconds: 5));
return flutterLocalNotificationsPlugin.showDailyAtTime(
0, 'plain title', 'plain body', Time(date.hour, date.minute, date.second),
platformChannelSpecifics, payload: 'item id 2');
}
特定の曜日、特定の時間で繰り返す
特定の曜日も追加したい場合はshowWeeklyAtDayAndTime
で曜日と時間を指定します。
Future _onNotification() {
var date = DateTime.now().add(Duration(seconds: 5));
return flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
0, 'plain title', 'plain body', Day.Sunday, Time(date.hour, date.minute, date.second),
platformChannelSpecifics, payload: 'item id 2');
}
おわりに
本当は起動時にNotificationを設定する方法も合わせて見ていたのですが、没としました。
今の所は状態変化のタイミングに合わせて通知設定をするというのが一番良いです。
面倒だから起動と同時に設定できるようにしたいなーと思ってたんですが、しょうがないので別の方法考えます。