How to unschedule previously scheduled local notifications after some time? - flutter

I am using flutter_local_notifications library to schedule local notifications every 1 hour. It is working as expected but now, I need a way to start/ stop the notification schedule (say, at push of a button).
I could not find anything regarding canceling scheduled notification requests in the documentation.

Cancelling/deleting a notification
// cancel the notification with id value of zero
await flutterLocalNotificationsPlugin.cancel(0);
// 0 is your notification id
Cancelling/deleting all notifications
await flutterLocalNotificationsPlugin.cancelAll();
And yes, this can cancel current and future notification. Just make sure correct notification id.

In the docs it is given in Cancelling/deleting a notification
await flutterLocalNotificationsPlugin.cancel(0);

I would do something like this:
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Below will give you any unpresented/scheduled notifications
final List<PendingNotificationRequest> pendingNotificationRequests =
await _flutterLocalNotificationsPlugin.pendingNotificationRequests();
for (var _pendingRequest in pendingNotificationRequests) {
_flutterLocalNotificationsPlugin.cancel(_pendingRequest.id);
}
Here _pendingRequest.id will give you the required notification id to cancel the specific notification request.
This is tested in Android. Will update the status on iOS soon.

await flutterLocalNotificationsPlugin.cancelAll(); //cancel future note
await flutterLocalNotificationsPlugin.pendingNotificationRequests(); //restart note
And your notification will show again.

If you want to cancel all notifications, then you have to do that using the following:
await flutterLocalNotificationsPlugin.cancelAll();
Also, if you want to cancel a specific notification, you can do this by using a specific ID of your notification:
await flutterLocalNotificationsPlugin.cancel(0); // 0 is your notification id
Where this notification ID came from ??
While you are declaring your notification to show on your screen you have to add some data like given below:
await _flutterLocalNotificationsPlugin.show(
0, // this is the notification id
message.notification.title,
message.notification.body,
notificationDetails,
payload: jsonEncode(message.data),
);
Here I am using Flutter Local Notification Plugin to show notifications!!!
Hope this will solve your problem.

Related

Error: No named parameter with the name 'onSelectNotification'

class HelperNotification {
static Future<void> initialize(FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async {
var androidInitialize = new AndroidInitializationSettings('notification_icon');
var iOSInitialize = new DarwinInitializationSettings();
var initializationsSettings = new InitializationSettings(android: androidInitialize, iOS: iOSInitialize);
flutterLocalNotificationsPlugin.initialize(initializationsSettings, onSelectNotification:(String? payload) async {
try{
if(payload != null && payload.isNotEmpty) {
// Get.toNamed(RouteHelper.getOrderDetailsRoute(int.parse(payload)));
}else {
// Get.toNamed(RouteHelper.getNotificationRoute());
}
}catch (e) {}
return;
});
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
I Found that there was an update with the local notification package but i was trying to impliment it and was unsuccessful can you help me figure it out?
Since version 10.0.0 of the flutter_local_notifications plugin, they removed onSelectNotification parameter. You can read more about it in their changelog:
Breaking change
callbacks have now been reworked. There are now the
following callbacks and both will pass an instance of the
NotificationResponse class onDidReceiveNotificationResponse: invoked
only when the app is running. This works for when a user has selected
a notification or notification action. This replaces the
onSelectNotification callback that existed before. For notification
actions, the action needs to be configured to indicate the the app or
user interface should be shown on invoking the action for this
callback to be invoked i.e. by specifying the
DarwinNotificationActionOption.foreground option on iOS and the
showsUserInterface property on Android. On macOS and Linux, as there's
no support for background isolates it will always invoke this callback
onDidReceiveBackgroundNotificationResponse: invoked on a background
isolate for when a user has selected a notification action. This
replaces the onSelectNotificationAction callback
Read more here: https://pub.dev/packages/flutter_local_notifications/changelog

Flutter save OneSignal notification message

I want to save the message from OneSignal push notification, so I can create a notification list.. but I have no idea to achieve this.. I open the Onesignal for flutter documentation but still no clue.. anyone can share some references to me?
In your main.dart make sure you have the listener setNotificationReceivedHandler activated. Then just save the notification.payload elements
OneSignal.shared.setNotificationReceivedHandler((notification) {
this.setState(() {
_debugLabelString =
"Received notification: \n${notification.jsonRepresentation().replaceAll("\\n", "\n")}";
newNotificationTitle = notification.payload.title;
newNotificationBody = notification.payload.body;
});
});

Flutter Background Processes Using Android Alarm Manager and Isolates

I'm trying to get a timer (down to the hundredths of seconds) to work in Flutter even when the app is closed. I initially tried to use isolates as I thought they would work yet after testing with a Pixel 4 running Android 11 I found that it was still not firing correctly when the app was closed. After some googleing I came across Android Alarm Manager and I have everything set up again yet it doesn't appear that the periodic function is firing correctly.
Heres the BLoC map for triggering the counter:
Stream<TimerState> _mapTimerStartedToState(TimerStarted start) async* {
AndroidAlarmManager.initialize();
port.listen((_) async => await _incrementCounter());
startCounter();
print(_counter);
yield TimerRunInProgress(start.duration);
}
Here's the startCounter() function:
void startCounter() async {
prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey(countKey)) {
await prefs.setInt(countKey, 0);
}
IsolateNameServer.registerPortWithName(
port.sendPort,
isolateName,
);
await AndroidAlarmManager.periodic(
Duration(milliseconds: 100),
// Ensure we have a unique alarm ID.
Random().nextInt(pow(2, 31)),
callback,
exact: true,
wakeup: true,
);
}
And then here's my callback:
static Future<void> callback() async {
print('Alarm fired!');
// Get the previous cached count and increment it.
final prefs = await
SharedPreferences.getInstance();
int currentCount = prefs.getInt(countKey);
await prefs.setInt(countKey, currentCount + 1);
// This will be null if we're running in the background.
print(currentCount);
uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
uiSendPort?.send(null);
}
Am I on the right path here? Can AndroidAlarmManager do what I'm trying to do? I'm not exactly sure why the isolate approach didn't work on its own either, the only explanation I got was that I needed to use AndroidAlarmManager. Now, the events aren't firing at the 100 ms rate as I told them to and are instead firing 1 to several minutes apart.
Android restricts the frequencies for alarms. You cannot schedule alarms as frequently as 100 milliseconds with AlarmManager.
Please refer the note in red background on : https://developer.android.com/reference/android/app/AlarmManager
Note: Beginning with API 19 (Build.VERSION_CODES.KITKAT) alarm
delivery is inexact: the OS will shift alarms in order to minimize
wakeups and battery use. There are new APIs to support applications
which need strict delivery guarantees; see setWindow(int, long, long,
android.app.PendingIntent) and setExact(int, long,
android.app.PendingIntent). Applications whose targetSdkVersion is
earlier than API 19 will continue to see the previous behavior in
which all alarms are delivered exactly when requested.

Notifications every day for a particular date range

So, I have an app where users are reminded to take medicines every day at a particular time for a certain interval of dates. For example, the user can choose to get a notification from September 16,2020 to September 18,2020 at some time of the day
My approach : I schedule a notification using the flutter_local_notifications package with showDailyAtTime() function. However, the problem I face is that, suppose I don't open the app again, there is no way to cancel the scheduled notification and thus, the notification pops up even after the specified date range. I would like the notifications to be offline, so Firebase doesn't seem to be an option.
You can solve the problem with FlutterLocalNotificationsPlugin.
The approach would be to call the method rescheduleNotifications every time you start the app. In the method all notifications are removed and the next notifications are set. In calculateNotificationTimes for example you calculate all notifications for the next 30 days. For example, all notifications on September 16, 2020 to September 18, 2020 each day at a time of your choice.
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
Future<void> rescheduleNotifications() async {
final localNotificationsPlugin = FlutterLocalNotificationsPlugin();
const initializationSettings = InitializationSettings(AndroidInitializationSettings('app_icon'), IOSInitializationSettings());
const androidChannelSpecifics = AndroidNotificationDetails('your channel id', 'your channel name', 'your channel description');
const iOSNotificationDetails = IOSNotificationDetails();
const notificationsDetails = NotificationDetails(androidChannelSpecifics, iOSNotificationDetails);
await localNotificationsPlugin.initialize(initializationSettings);
await localNotificationsPlugin.cancelAll();
// Calculate the next notifications.
final notificationTimes = calculateNotificationTimes();
var _currentNotificationId = 0;
for (final time in notificationTimes) {
localNotificationsPlugin.schedule(
_currentNotificationId++,
"It's time to take your medicine.",
'Take the red pill',
time,
notificationsDetails,
androidAllowWhileIdle: true,
);
}
}
On iOS there is a limit that you can only have 64 notifications enabled. The disadvantage of this method on iOS is that if the user does not open the app after 64 notifications, no notification will be displayed. Which is fine, I think, because it seems that the user does not use the app anymore.
Did not test the code.

Flutter: How to check if app is running on background from firebase messaging plugins onBackgroundMessage

I'm using firebase_messaging plugin to register a callback handler with onBackgroundMessage for my data-only payload of firebase messaging.
If the app is in foreground or in background, the normal way of operation is using sockets to get the data from network and show notification from the app.
But when the app is in killed state, I would like to show the notification by fetching the data from network.
But these operations conflicts when the app is in background as onBackgroundMessage is getting called in background also.
If I'm not wrong, the handler is running on a separate isolate and it has no access to the main contents.
So how can I differentiate the killed and background state of the app from this isolated function?
You can use IsolateNameServer to register a ReceiverPort from the foreground when it is running and remove it when the foreground is not running. Then on the background isolate check if it exists and if so redirect the FCM message through the port to the foreground for handling on foreground.
Something along the lines of this:
const FOREGROUND_ISOLATE_PORT_NAME = 'foreground_port';
class NotificationManager {
ReceivePort? _foregroundReceivePort;
StreamSubscription<RemoteMessage>? _fcmMessageSubscription;
init() async {
FirebaseMessaging.onBackgroundMessage(_fcmMessageHandlerBackground);
_fcmMessageSubscription = FirebaseMessaging.onMessage.listen(_fcmMessageHandlerForeground);
_foregroundReceivePort = ReceivePort();
IsolateNameServer.registerPortWithName(
_foregroundReceivePort!.sendPort,
FOREGROUND_ISOLATE_PORT_NAME,
);
_foregroundReceivePort!.listen((message) {
if (message is RemoteMessage) {
log('got fcm message for handling in foreground');
_fcmMessageHandlerForeground(message);
}
});
}
shutdown() async {
_fcmMessageSubscription?.cancel();
IsolateNameServer.removePortNameMapping(FOREGROUND_ISOLATE_PORT_NAME);
_foregroundReceivePort!.close();
_foregroundReceivePort = null;
}
}
With these two top level functions:
Future<void> _fcmMessageHandlerForeground(RemoteMessage message) async {
// ... handle message in foreground ...
}
Future<void> _fcmMessageHandlerBackground(RemoteMessage message) async {
final foreground = IsolateNameServer.lookupPortByName(FOREGROUND_ISOLATE_PORT_NAME);
if (foreground != null) {
log("redirecting FCM message to foreground");
foreground.send(message);
} else {
// ... handle message in background ...
}
}