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.
Related
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.
I am trying to use the plugin, flutter_local_notifications to send recurring notifications to the user, every day but the resource available for the plugin consists of only code and is incomprehensible, I have done the setup for the plugin as follows:
added the dependency of flutter_local_notifications
added the permission code for iOS in the AppDelegate.swift
I did this much by referring to numerous resources like medium and a few other sites, so can someone please write a method which sends the user (android & iOS) a recurring notification everyday? Thank you!
After writing that comment I did a bit of research and this is working for me so far:
Future<void> showAlertNotification() async {
var time = Time(8, 0, 0);
var androidChannel = AndroidNotificationDetails(
'channelID', 'channelName', 'channelDescription',
importance: Importance.defaultImportance,
priority: Priority.defaultPriority,
playSound: true);
var iosChannel = IOSNotificationDetails();
var platformChannel =
NotificationDetails(android: androidChannel, iOS: iosChannel);
await flutterLocalNotificationsPlugin.showDailyAtTime(2, 'notification title',
'message here', time, platformChannel,
payload: 'new payload'));
}
You should not use showDailyAtTime as of now it has quite a number of problems like syncing up with local time zones. So instead you should use the zoned schedule but you should first initialize it with your local time zone it can be done with another package called flutter_native_timezone. This is the workflow I used:
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
Future<void> showNotif() async {
tz.initializeTimeZones();
String dtz = await FlutterNativeTimezone.getLocalTimezone();
if (dtz == "Asia/Calcutta") {
dtz = "Asia/Kolkata";
}
final localTimeZone = tz.getLocation(dtz);
tz.setLocalLocation(localTimeZone);
await _flutterLocalNotificationsPlugin.zonedSchedule(..., matchDateTimeComponents: DateTimeComponents.time);
}
// The ... above is the usual parameters that I didn't write.
I want to be notified on certain days of the week at a specific time using the local notices package.
Certain days of the week can be notified. But I don't know how to be notified on certain days of the week.
The Day.Monday part contains only the value, but no array.
I'd like to add more than two days of the week to this part.
Please help me with how to solve this problem.
Thank you.
Package Example Source ( https://pub.dev/packages/flutter_local_notifications/example ) :
Future<void> _showWeeklyAtDayAndTime() async {
var time = Time(20, 24, 0);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'show weekly channel id',
'show weekly channel name',
'show weekly description');
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
0,
'show weekly title',
'Weekly notification shown on Monday at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}',
Day.Monday,
time,
platformChannelSpecifics);
}
There is no way to schedule weekly notifications for some specific days in this package. The only thing you can do is to schedule same notification for each day separately.
I am working on a flutter app, In which using flutter_local_notifications for the local notification. Weekly notification is working fine for selecting the only day on weekdays. Now I want to pick multiple days on weekdays like (Monday, Thursday, and Saturday). I couldn't find any solution to implement. Sharing my sample code.
showNotificationWeekly() {
var time = Time(10, 0, 0);
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'id',
'name',
'description',
);
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
0,
'show weekly title',
'Weekly notification',
Day.Monday,
time,
platformChannelSpecifics,
);
}
Please let me know, If any solution for this so that I can set one notification for multiple days
I think you will need to schedule multiple Notifications, one for each day :/
Thanks, It require unique notification ID for each notification.
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.