I want to schedule daily notifications at a specific daytime with the android_alarm_manager_plus and the local_notifications plugins.
However, the notifications come in very irregulary. The delay of the notifications increases each consecutive day.
For example, the app should send a reminder everyday at 8:00 am. The first day, the notification comes in at around the right time. The next day, it sends it 15 minutes delayed and the following days, no notification gets send at all.
I tested this on a Motorola Moto G9 Play with Android 11 and I have deactivated the energy-saving mode and made sure that battery-optimizations are turned off for this app.
In the following I created a litte (hopefully) reproducable example:
// initializing similar to the official example
Future<FlutterLocalNotificationsPlugin> _initNotificationsPlugin() async {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('flutter_logo');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final MacOSInitializationSettings initializationSettingsMacOS =
MacOSInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: initializationSettingsMacOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
return flutterLocalNotificationsPlugin;
}
Future<void> scheduleNotifications() async {
int id = 0;
AndroidAlarmManager.periodic(Duration(days: 1), id, sendNotification,
exact: true,
allowWhileIdle: true,
rescheduleOnReboot: true,
wakeup: true);
log("Notifications scheduled");
}
// Top-level callback for the AndroidAlarmManager
Future<void> sendNotification() async {
FlutterLocalNotificationsPlugin flutterNotificationsPlugin =
await _initNotificationsPlugin();
await flutterNotificationsPlugin.cancelAll();
final now = DateTime.now();
int id =
now.second + now.minute * 60 + now.hour * 60 * 60; // Daytime in seconds
// Copied from the example with edited channel id, etc.
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'my very own channel id', 'my very own channel name',
channelDescription: 'my channel description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
//
final body = "Notification sent at ${now.hour}:${now.minute}:${now.second}";
await flutterNotificationsPlugin.show(
id, "Test notification", body, platformChannelSpecifics);
log(body);
}
Future<void> cancelAllAlarmsAndNotifications() async {
await AndroidAlarmManager.cancel(0);
}
And when the user clicks a button, the following code is executed:
onPressed: () {
cancelAllAlarmsAndNotifications().then((value) {
scheduleNotifications();
});
},
Also, the main method:
void main() {
WidgetsFlutterBinding.ensureInitialized();
AndroidAlarmManager.initialize().then((value) {
runApp(const MyApp());
});
}
I really hope that you see some mistake I make that is easy fixable :)
Thanks in advance!
Related
android only
I tried NotificationServices Scheduled method with _flutterLocalNotificationsPlugin.periodicallyShow and it doesn't seems to work.
also how to test it in a android emulator (changing the date and time on the device works or not)
notification_service.dart
class NotificationServices {
FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final AndroidInitializationSettings _androidInitializationSettings =
AndroidInitializationSettings('logo1');
void initialiseNotifications() async {
InitializationSettings initializationSettings = InitializationSettings(
android: _androidInitializationSettings,
);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
void scheduleNotifications(String title, String body) async {
int hour = 19;
var ogValue = hour;
int minute = 05;
var time = Time(hour,minute,20);
AndroidNotificationDetails androidNotificationDetails =
const AndroidNotificationDetails(
'channelId',
'channelName',
importance: Importance.max,
priority: Priority.high,
);
NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails,
);
await _flutterLocalNotificationsPlugin.periodicallyShow(
0, 'title', 'body', RepeatInterval.daily, notificationDetails);
}
void stopNotifications() async{
_flutterLocalNotificationsPlugin.cancel(0);
}
}
main.dart
NotificationServices notificationServices = NotificationServices();
notificationServices.initialiseNotifications();
notificationServices.scheduleNotifications('Daily Reminder', 'You have a ToDo list to complete');
I'm working on a Flutter App with Firebase Messaging. The notifications work perfectly fine on iOS. On Android, they work when the app is in the foreground, but are not displayed when the app is in background or terminated. I could already rule out doze mode, as the app has been put to background just seconds before triggering the message. The logfile also says, the message is received by the devices, but somehow not displayed.
The problem also only exists when the app is installed via .apk and not when I build directly with Android Studio, so I thought maybe the fingerprinting could cause the problem. But the messages are received by the device and just not displayed -> so that's not it either, right?
I use firebase_messaging: ^12.0.0 as the plugin.
Here is the implementation of my background handler:
static Future<void> setUp() async {
await Firebase.initializeApp(
name: 'Firebase-App',
options: DefaultFirebaseOptions.currentPlatform,
);
//... iOS stuff
//Enable Foreground-Messages for Android
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.max,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await configLocalNotification(flutterLocalNotificationsPlugin);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
//Display Foreground-Message on Android
flutterLocalNotificationsPlugin.show(
message.hashCode,
Uri.decodeComponent(message.data['title']).replaceAll('+', ' '),
Uri.decodeComponent(message.data['message']).replaceAll('+', ' '),
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '#mipmap/ic_launcher',
importance: Importance.max,
),
),
payload: json.encode(message.data),
);
});
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
}
static Future<void> _firebaseMessagingBackgroundHandler(
RemoteMessage message) async {
await Firebase.initializeApp();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.max,
);
await flutterLocalNotificationsPlugin.show(
message.hashCode,
Uri.decodeComponent(message.data['title']).replaceAll('+', ' '),
Uri.decodeComponent(message.data['message']).replaceAll('+', ' '),
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '#mipmap/ic_launcher',
importance: Importance.max,
// other properties...
),
),
payload: json.encode(message.data),
);
}
static Future<void> configLocalNotification(
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async {
const initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
const initializationSettingsIOS = IOSInitializationSettings();
const initializationSettings = InitializationSettings(
iOS: initializationSettingsIOS, android: initializationSettingsAndroid);
//Handle on click event when App is in foreground or background
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
//handle on click event when app is terminated
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
final String? selectedNotificationPayload =
notificationAppLaunchDetails!.payload;
debugPrint(
'notification payload on launch: $selectedNotificationPayload');
await onSelectNotification(selectedNotificationPayload);
}
}
Thanks in advance for your help, I'm really at the end of the line here!
I am developing water drinking reminder app with flutter.
I want to schedule a list of time specified local notifications that user can add to this list and delete from this list. like this
Any help would appreciate, Thanks.
After hours of research I have solved this problem.
The full code is below.
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
class NotificationHelper {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
/// Initialize notification
initializeNotification() async {
_configureLocalTimeZone();
const IOSInitializationSettings initializationSettingsIOS = IOSInitializationSettings();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings("ic_launcher");
const InitializationSettings initializationSettings = InitializationSettings(
iOS: initializationSettingsIOS,
android: initializationSettingsAndroid,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
/// Set right date and time for notifications
tz.TZDateTime _convertTime(int hour, int minutes) {
final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
tz.TZDateTime scheduleDate = tz.TZDateTime(
tz.local,
now.year,
now.month,
now.day,
hour,
minutes,
);
if (scheduleDate.isBefore(now)) {
scheduleDate = scheduleDate.add(const Duration(days: 1));
}
return scheduleDate;
}
Future<void> _configureLocalTimeZone() async {
tz.initializeTimeZones();
final String timeZone = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZone));
}
/// Scheduled Notification
scheduledNotification({
required int hour,
required int minutes,
required int id,
required String sound,
}) async {
await flutterLocalNotificationsPlugin.zonedSchedule(
id,
'It\'s time to drink water!',
'After drinking, touch the cup to confirm',
_convertTime(hour, minutes),
NotificationDetails(
android: AndroidNotificationDetails(
'your channel id $sound',
'your channel name',
channelDescription: 'your channel description',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(sound),
),
iOS: IOSNotificationDetails(sound: '$sound.mp3'),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
payload: 'It could be anything you pass',
);
}
/// Request IOS permissions
void requestIOSPermissions() {
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
cancelAll() async => await flutterLocalNotificationsPlugin.cancelAll();
cancel(id) async => await flutterLocalNotificationsPlugin.cancel(id);
}
add your custom times like this
for (int i = 0; i < _provider.getScheduleRecords.length; i++) {
_notificationHelper.scheduledNotification(
hour: int.parse(_provider.getScheduleRecords[i].time.split(":")[0]),
minutes: int.parse(_provider.getScheduleRecords[i].time.split(":")[1]),
id: _provider.getScheduleRecords[i].id,
sound: 'sound0',
);
}
You can use flutter_local_notifications plugin, it can send scheduled, instant and also repeating notifications
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
'scheduled title',
'scheduled body',
tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)),
const NotificationDetails(
android: AndroidNotificationDetails(
'your channel id', 'your channel name',
channelDescription: 'your channel description')),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
This example will schedule a notification that appears after 5 seconds.
Try Awesome Notifications.
It has many features including seconds precision scheduled notifications.
I want to bring an app in the background to the foreground. This link use a package that is not working and although the author has done a tremendous job it has seen no updates for 11 months.
So I am looking for a solution to bring the app in the background to the foreground or even (re)launch the app. I have spent countless hours trying different packages and none of them work, here is the list:
import 'package:bringtoforeground/bringtoforeground.dart';
import 'package:android_intent/android_intent.dart';
import 'package:flutter_appavailability/flutter_appavailability.dart';
import 'flutterIntent.dart';
flutterIntent.dart is thanks to this repository: https://github.com/sunsetjesus/flutter_intent
import 'dart:async';
import 'package:flutter/services.dart';
class FlutterIntent {
static const MethodChannel _channel = const MethodChannel('flutter_intent');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static Future<void> openApp(String appId, String targetActivity) async {
await _channel.invokeMapMethod('openApp', {"appId": appId,"targetActivity": targetActivity});
}
}
Not to mention the last of solution for IOS.
I am quite astonished that there are so many good packages but there seems to be no default solution to open an app (which we can do by tapping twice on the icon...). It is very simple to start another app when coding for Windows or Linux platform so I am really amazed that it requires so much effort when it comes to mobile.
Thank you very much for any help,
PS: Actually I don't even need to bring the app back to the foreground, originally I was trying to have await launch(url); on notification received, so launching chrome/firefox/safari with the url (that I get with no problem) would be good enough in my usecase.
I solved it myself thanks to flutter local notification package, example below:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
Future _showNotification(FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async {
var androidPlatformChannelSpecifics = new AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.max, priority: Priority.high);
var iOSPlatformChannelSpecifics = new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0,
'New Notification',
'Flutter is awesome',
platformChannelSpecifics,
payload: 'This is notification detail Text...',
);
}
static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
static onBackgroundMessage(SmsMessage message) async {
String encodedUrl = message.body;
debugPrint("onBackgroundMessage called $encodedUrl");
print('app available');
await _showNotification(flutterLocalNotificationsPlugin);
}
Future onSelectNotification(String payload) async {
showDialog(
context: context,
builder: (_) {
return new AlertDialog(
title: Text("Your Notification Detail"),
content: Text("Payload : $payload"),
);
},
);
}
void localNotification() {
var initializationSettingsAndroid =
new AndroidInitializationSettings('app_icon');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
}
And in StatefulWidget:
#override
void initState() {
super.initState();
localNotification();
}
I am working on a project, I have used Flutter Local Notifications to show periodic notifications,
I'm trying to figure out if there is a way to not to launch the app on notification click, for example, when a user clicks on a notification, it should just disappear from the notification panel.
This is the code I tried so far,
Initialising,
var initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = IOSInitializationSettings();
var initializationSettings = InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
_notificationPlugin = FlutterLocalNotificationsPlugin();
_notificationPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
Calling,
Future getPeriodicNoification() async {
print("hello");
// Show a notification every minute with the first appearance happening a minute after invoking the method
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id',
'your channel name',
'your channel description',
importance: Importance.Max,
priority: Priority.High,
);
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await _notificationPlugin.periodicallyShow(0, 'repeating title',
'repeating body', RepeatInterval.EveryMinute, platformChannelSpecifics);
}
Callback function(On notification click)
Future onSelectNotification(String payload) async {
ThisisAnAsyncFunction();
}
Please help.
You can use the close function under BehaviourSubject that you use when adding the payload.
static final onNotifications = BehaviorSubject<String?>();
then call this when selecting the notification.
onSelectNotification() => onNotifications.close();