Skip to content

Commit 3bdbdb7

Browse files
committed
doc uodates and clean up sample
1 parent d0891e0 commit 3bdbdb7

File tree

5 files changed

+52
-39
lines changed

5 files changed

+52
-39
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# [0.4.1]
2+
* [Android/iOS] New `onShowNotification` callback for when a notification is shown that can handled when calling the `initialize` function. This uses the latest changes to the Flutter engine that supports headless execution i.e. the ability to execute Dart code without the app running. See [here](https://github.com/flutter/flutter/issues/6192) for a link to the issue in the main Flutter repository about this. Note that the function handling this callback must be top-level function as this is requirement for executing headless Dart code. The example for the plugin has been updated to provide sample code on to handle the callback.
3+
* **BREAKING CHANGE** renamed the `selectNotification` callback exposed by the `initialize` function to `onSelectNotification`
4+
* Address issue [115](https://github.com/MaikuB/flutter_local_notifications/issues/115) by adding validation to the notification ID values. This ensure they're within the range of a 32-bit integer as notification IDs on Android need to be within that range. Note that an `ArgumentError` is thrown when a value is out of range.
5+
16
# [0.4.0]
27
* [Android] Fix issue [112](https://github.com/MaikuB/flutter_local_notifications/issues/112) where big picture notifications wouldn't show
38

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ A cross platform plugin for displaying local notifications.
2020
* Specify a custom notification sound
2121
* Ability to handle when a user has tapped on a notification, when the app is the foreground, background or terminated
2222
* Determine if an app was launched due to tapping on a notification
23+
* [Android/iOS 10+] Receive callback when a notification is shown
2324
* [Android] Configuring the importance level
2425
* [Android] Configuring the priority
2526
* [Android] Customising the vibration pattern for notifications
@@ -67,10 +68,11 @@ var initializationSettings = new InitializationSettings(
6768
initializationSettingsAndroid, initializationSettingsIOS);
6869
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
6970
flutterLocalNotificationsPlugin.initialize(initializationSettings,
70-
selectNotification: onSelectNotification);
71+
onSelectNotification: onSelectNotification
72+
onShowNotification: onShowNotification);
7173
```
7274

73-
Here we specify we have specified the default icon to use for notifications on Android (refer to the Android Integration section) and designated the function (onSelectNotification) that should fire when a notification has been tapped on. Specifying this callback is entirely optional. In this example, it will trigger navigation to another page and display the payload associated with the notification.
75+
Here we specify we have specified the default icon to use for notifications on Android (refer to the Android Integration section) and designated the function (onSelectNotification) that should fire when a notification has been tapped on. Specifying this callback is entirely optional. In this example, it will trigger navigation to another page and display the payload associated with the notification. It is also possible to handle the `onShowNotification` that will invoke code when a notification is shown. Note that the function handling this callback must be top-level function as this is requirement for executing headless Dart code.
7476

7577
```
7678
Future onSelectNotification(String payload) async {
@@ -256,22 +258,28 @@ This should cover the basic functionality. Please check out the `example` direct
256258

257259
## Android Integration
258260

259-
If your application needs the ability to schedule notifications then you need to request permissions to be notified when the phone has been booted as scheduled notifications uses AlarmManager to determine when notifications should be displayed. However, they are cleared when a phone has been turned off. Requesting permission requires adding the following to the manifest
261+
If your application needs the ability to schedule notifications then you need to request permissions to be notified when the phone has been booted as scheduled notifications uses the `AlarmManager` API to determine when notifications should be displayed. However, they are cleared when a phone has been turned off. Requesting permission requires adding the following to the manifest
260262

261263
```xml
262264
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
263265
```
264266

265-
Developers will also need to add the following so that plugin can handle displaying scheduled notifications and reschedule notifications upon a reboot
267+
Developers will also need to add the following so that plugin can handle displaying scheduled notifications
266268

267269
```xml
268270
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
271+
```
272+
273+
As Android normally will not keep anything scheduled via the `AlarmManager` API upon reboot, the following is also needed to ensure scheduled notifications remain scheduled upon a reboot
274+
275+
```xml
269276
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
270277
<intent-filter>
271278
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
272279
</intent-filter>
273280
</receiver>
274281
```
282+
275283
If the vibration pattern of an Android notification will be customised then add the following
276284

277285
```xml

example/lib/main.dart

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import 'package:path_provider/path_provider.dart';
1010
import 'package:shared_preferences/shared_preferences.dart';
1111
import 'package:rxdart/rxdart.dart';
1212

13-
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
14-
Future<SharedPreferences> sharedPrefs = SharedPreferences.getInstance();
13+
var flutterLocalNotificationsPlugin;
14+
var sharedPrefs = SharedPreferences.getInstance();
1515
var counterSubject = PublishSubject<int>();
16+
var portName = 'notification_shown_port';
17+
var shownCounterSharedPrefKey = 'shownCounter';
1618

1719
/// IMPORTANT: running the following code on its own won't work as there is setup required for each platform head project.
1820
/// Please download the complete example app from the GitHub repository where all the setup has been done
@@ -30,20 +32,21 @@ void main() async {
3032
);
3133
}
3234

33-
Future onNotification(int id, String title, String body, String payload) async {
35+
/// Top-level function to handle when a notification is shown
36+
Future onShowNotification(
37+
int id, String title, String body, String payload) async {
3438
print(
3539
'on notification callback triggered with id: $id, title: $title, body: $body, payload: $payload');
3640
// update a counter in shared preferences to track how many times a notification has been shown
3741
// this example app will only display the counter on a cold start of the app to demonstrate headless execution
3842
if (Platform.isAndroid) {
3943
// IMPORTANT: Flutter currently only supports executing headless Dart code that uses other plugins on Android
4044
var sharedPreferences = await sharedPrefs;
41-
var shown = (sharedPreferences.getInt('shownCounter') ?? 0) + 1;
42-
sharedPreferences.setInt('shownCounter', shown);
45+
var shown = (sharedPreferences.getInt(shownCounterSharedPrefKey) ?? 0) + 1;
46+
sharedPreferences.setInt(shownCounterSharedPrefKey, shown);
4347

44-
// use to send updates that can be handled in the UI
45-
final SendPort send =
46-
IsolateNameServer.lookupPortByName('notification_shown_port');
48+
// required so updates can be handled in the UI
49+
final SendPort send = IsolateNameServer.lookupPortByName(portName);
4750
send?.send(shown);
4851
}
4952
}
@@ -67,14 +70,14 @@ class _HomePageState extends State<HomePage> {
6770
initializationSettingsAndroid, initializationSettingsIOS);
6871
flutterLocalNotificationsPlugin.initialize(initializationSettings,
6972
onSelectNotification: onSelectNotification,
70-
onNotification: onNotification);
73+
onShowNotification: onShowNotification);
7174
sharedPrefs.then((sharedPreferences) {
72-
var counter = sharedPreferences.getInt('shownCounter') ?? 0;
75+
var counter = sharedPreferences.getInt(shownCounterSharedPrefKey) ?? 0;
7376
counterSubject.sink.add(counter);
7477
});
7578

76-
IsolateNameServer.registerPortWithName(
77-
port.sendPort, 'notification_shown_port');
79+
// used to handle that a notification was shown while app was running and we need to update the counter
80+
IsolateNameServer.registerPortWithName(port.sendPort, portName);
7881
port.listen((dynamic data) {
7982
counterSubject.sink.add(data);
8083
});
@@ -100,23 +103,6 @@ class _HomePageState extends State<HomePage> {
100103
'Tap on a notification when it appears to trigger navigation'),
101104
),
102105
// NOTE: the following text is demonstrate headless execution with plugins work in Android
103-
/*new Padding(
104-
padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0),
105-
child: new FutureBuilder(
106-
future: sharedPrefs,
107-
builder:
108-
(BuildContext context, AsyncSnapshot snapshot) {
109-
if (snapshot.hasData) {
110-
SharedPreferences sharedPreferences = snapshot.data;
111-
var counter =
112-
sharedPreferences.getInt('shownCounter') ?? 0;
113-
return new Text(
114-
'Shown ${counter.toString()} Android notifications since the last cold start');
115-
} else {
116-
return CircularProgressIndicator();
117-
}
118-
},
119-
)),*/
120106
new Padding(
121107
padding: new EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 8.0),
122108
child: new StreamBuilder(

lib/src/flutter_local_notifications.dart

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ typedef Future<dynamic> SelectNotificationCallback(String payload);
66
/// Signature of callback passed to [initialize].
77
/// Callback is triggered when a notification is shown. Note that on iOS this only works on iOS 10+.
88
/// The callback must be a top-level or static method
9-
typedef Future<dynamic> NotificationCallback(
9+
typedef Future<dynamic> ShowNotificationCallback(
1010
int id, String title, String body, String payload);
1111

1212
/// The available intervals for periodically showing notifications
@@ -78,7 +78,7 @@ class FlutterLocalNotificationsPlugin {
7878
/// Initializes the plugin. Call this method on application before using the plugin further
7979
Future<bool> initialize(InitializationSettings initializationSettings,
8080
{SelectNotificationCallback onSelectNotification,
81-
NotificationCallback onNotification}) async {
81+
ShowNotificationCallback onShowNotification}) async {
8282
onSelectNotification = onSelectNotification;
8383
var serializedPlatformSpecifics =
8484
_retrievePlatformSpecificInitializationSettings(initializationSettings);
@@ -91,11 +91,11 @@ class FlutterLocalNotificationsPlugin {
9191
var headlessInitializationSettings = <String, dynamic>{
9292
'callbackDispatcher': callback.toRawHandle()
9393
};
94-
if (onNotification != null) {
94+
if (onShowNotification != null) {
9595
headlessInitializationSettings['onNotificationCallbackDispatcher'] =
96-
PluginUtilities.getCallbackHandle(onNotification).toRawHandle();
96+
PluginUtilities.getCallbackHandle(onShowNotification).toRawHandle();
9797
}
98-
print(result.toString());
98+
9999
await _channel.invokeMethod(
100100
'initializeHeadlessService', headlessInitializationSettings);
101101
return result;
@@ -111,6 +111,7 @@ class FlutterLocalNotificationsPlugin {
111111
Future show(int id, String title, String body,
112112
NotificationDetails notificationDetails,
113113
{String payload}) async {
114+
_validateId(id);
114115
var serializedPlatformSpecifics =
115116
_retrievePlatformSpecificNotificationDetails(notificationDetails);
116117
await _channel.invokeMethod('show', <String, dynamic>{
@@ -124,6 +125,7 @@ class FlutterLocalNotificationsPlugin {
124125

125126
/// Cancel/remove the notification with the specified id. This applies to notifications that have been scheduled and those that have already been presented.
126127
Future cancel(int id) async {
128+
_validateId(id);
127129
await _channel.invokeMethod('cancel', id);
128130
}
129131

@@ -136,6 +138,7 @@ class FlutterLocalNotificationsPlugin {
136138
Future schedule(int id, String title, String body, DateTime scheduledDate,
137139
NotificationDetails notificationDetails,
138140
{String payload}) async {
141+
_validateId(id);
139142
var serializedPlatformSpecifics =
140143
_retrievePlatformSpecificNotificationDetails(notificationDetails);
141144
await _channel.invokeMethod('schedule', <String, dynamic>{
@@ -153,6 +156,7 @@ class FlutterLocalNotificationsPlugin {
153156
Future periodicallyShow(int id, String title, String body,
154157
RepeatInterval repeatInterval, NotificationDetails notificationDetails,
155158
{String payload}) async {
159+
_validateId(id);
156160
var serializedPlatformSpecifics =
157161
_retrievePlatformSpecificNotificationDetails(notificationDetails);
158162
await _channel.invokeMethod('periodicallyShow', <String, dynamic>{
@@ -170,6 +174,7 @@ class FlutterLocalNotificationsPlugin {
170174
Future showDailyAtTime(int id, String title, String body,
171175
Time notificationTime, NotificationDetails notificationDetails,
172176
{String payload}) async {
177+
_validateId(id);
173178
var serializedPlatformSpecifics =
174179
_retrievePlatformSpecificNotificationDetails(notificationDetails);
175180
await _channel.invokeMethod('showDailyAtTime', <String, dynamic>{
@@ -188,6 +193,7 @@ class FlutterLocalNotificationsPlugin {
188193
Future showWeeklyAtDayAndTime(int id, String title, String body, Day day,
189194
Time notificationTime, NotificationDetails notificationDetails,
190195
{String payload}) async {
196+
_validateId(id);
191197
var serializedPlatformSpecifics =
192198
_retrievePlatformSpecificNotificationDetails(notificationDetails);
193199
await _channel.invokeMethod('showWeeklyAtDayAndTime', <String, dynamic>{
@@ -228,4 +234,11 @@ class FlutterLocalNotificationsPlugin {
228234
Future _handleMethod(MethodCall call) {
229235
return onSelectNotification(call.arguments);
230236
}
237+
238+
void _validateId(int id) {
239+
if (id > 0x7FFFFFFF || id < -0x80000000) {
240+
throw ArgumentError(
241+
'id must fit within the size of a 32-bit integer i.e. in the range [-2^31, 2^31 - 1]');
242+
}
243+
}
231244
}

pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: flutter_local_notifications
22
description: A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform.
3-
version: 0.4.0
3+
version: 0.4.1
44
author: Michael Bui <[email protected]>
55
homepage: https://github.com/MaikuB/flutter_local_notifications
66

@@ -9,6 +9,7 @@ dependencies:
99
sdk: flutter
1010
meta: ^1.0.4
1111
platform: ^2.0.0
12+
fixnum: ^0.10.8
1213

1314
dev_dependencies:
1415
flutter_test:

0 commit comments

Comments
 (0)