I have an app that schedules alarms, which trigger flutter local notifications. When pressing one of the buttons on these notifications, the database gets updated to reflect that you've turned off the alarm.
However, when the app is terminated/closed down, it seems as if no code gets executed and the database doesn't get updated.
The notification does get triggered, it's when the user presses a button inside of it that no code gets executed. Looks something like this
I'd like to know if what I'm trying is even possible, because at this point I'm not sure.
The notifications are created using the zonedSchedule from the flutter local notifications plugin.
The app does not get opened when pressing one of the buttons in the notification.
The code is as follows:
class LocalNoticationsService {
LocalNoticationsService(
this._notificationsPlugin,
);
final FlutterLocalNotificationsPlugin _notificationsPlugin;
Future<void> init() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings(
'img/path',// path to notification icon
);
final DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
defaultPresentAlert: true,
defaultPresentSound: true,
notificationCategories: [
DarwinNotificationCategory(
'category',
options: {
DarwinNotificationCategoryOption.allowAnnouncement,
},
actions: [
DarwinNotificationAction.plain(
'snoozeAction',
'snooze',
),
DarwinNotificationAction.plain(
'confirmAction',
'confirm',
options: {
DarwinNotificationActionOption.authenticationRequired,
},
),
],
),
],
);
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await _notificationsPlugin.initialize(
initializationSettings,
onSelectNotification: _onSelectedNotification,
onSelectNotificationAction: onSelectNotificationAction,
);
final notificationOnLaunchDetails = await _notificationsPlugin.getNotificationAppLaunchDetails();
if (notificationOnLaunchDetails?.didNotificationLaunchApp ?? false) {
_onSelectedNotification(notificationOnLaunchDetails!.payload);
}
}
void _onSelectedNotification(String? payload) {
// Not relevant to this problem
}
Future<bool> _confirmAlarm(AlarmPayLoad alarmPayload) async {
// Update database
}
}
// Top level function
Future<void> onSelectNotificationAction(NotificationActionDetails details) async {
await Firebase.initializeApp();
final localNotificationsService = LocalNoticationsService(
FlutterLocalNotificationsPlugin()
);
await localNotificationsService.init();
final alarmPayload = AlarmPayLoad.fromJson(details.payload);
await localNotificationsService._confirmAlarm(alarmPayload)
}
Due to this all running in the background, I've been unable to find a way to log things. I've tried using the http package to send post request to a webhook with some data, but these do not seem to trigger when the app is terminated, but do work when in the app is in the foreground or in the background(not closed).
Any post or videos I've found online about flutter local notifications don't seem to cover this problem or are using firebase push notifications and the associated package for background handling.
I'd be very interested to hear any feedback on what might be going wrong or what might be the solution and how handling background notifications works. If you need additional information on the code, feel free to ask.
If you want to run it on background, follow the link here.
Related
I am using awesome notifications in Flutter to show local notifications. My plan is to have an action button in a notification, which changes some values in the shared preferences in the background (without opening the app) when pressed. Is this even possible to do?
I tried using onActionReceivedMethod-listener and getInitialNotificationAction-method in my main function after initializing awesome notifications:
AwesomeNotifications().setListeners(onActionReceivedMethod: (action) async{
print(action.body);
});
// OR
ReceivedAction? receivedAction = await AwesomeNotifications().getInitialNotificationAction(
removeFromActionEvents: false
);
if (receivedAction?.body != null){
print(receivedAction.body);
}
Both of them worked (separetly used) only when the app had already started, but they also gave this error:
Awesome Notifications: A background message could not be handled in Dart because there is no dart background handler registered. (BackgroundService:58)
But how I could get it working when the app is not opened? Can I create a backgroung handler without Firebase, or is there some other way to achieve this?
Here is my code, how I create the notification:
static Future<void> createSimpleNotification() async {
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: 1,
channelKey: 'important_channel',
title: 'Title',
body: 'Test',
largeIcon: 'asset://assets/iconPhoto.png'),
actionButtons: [
NotificationActionButton(
key:'key',
label: 'label',
actionType: ActionType.SilentBackgroundAction)]
);
}
I found the answer from here: Cannot find AwesomeNotifications().actionStream
I implemented it like this:
#pragma("vm:entry-point")
Future<void> _onActionReceivedMethod(ReceivedAction action) async {
print('It works');
}
And it worked both when the app was in the background or in the foreground.
i hope you're doing good. I'm trying to solve one problem: everything woks fine, the notifications works fine on iOS but when i try to execute a code ( showDialog ) for the notification it doesnt works. I'm using onSelectedNotification: function to execute something, in Android it works perfectly but in iOS it's just not opening. Follow the initState
FirebaseMessaging.onMessage.listen((RemoteMessage message) { RemoteNotification? notification=message.notification;
AndroidNotification? android=message.notification?.android;
if(notification!=null && android!=null){
fltNotification.show(
notification.hashCode, notification.title, notification.
body, generalNotificationDetails);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {// I've tried to use the function here too but it not works
});
Follow the configs for HomePage:
var androiInit = AndroidInitializationSettings('#mipmap/ic_launcher');//for logo
var iosInit = IOSInitializationSettings();
IOSNotificationDetails(presentAlert: true, presentBadge: true, presentSound: true);
var initSetting=InitializationSettings(android: androiInit,iOS:
iosInit);
flutterLocalNotificationsPlugin.initialize(initSetting, onSelectNotification: onSelectNotification);
And i'm using this onSelectNotification on flutterlocalnotificationsplugin to run my function ( showDialog ) but in IOS it not works. What can i do? Am I missing something?
I use the Firebase Cloud Messaging (FCM) in my Flutter project. The following packages:
firebase_messaging: ^8.0.0-dev.15
flutter_local_notifications: ^4.0.1+2
I know that I can handle click on push-notification when app is in background in the following way:
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
//TODO
});
and it works fine, but how to handle click on push-notification when application is in foreground? Can't find in the documentation as well. Could you please help me. Thanks in advance.
you can set the callback function on initializing
await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
),
onSelectNotification: handleClickNotification,
);
handleClickNotification(String? payload) {
//your code
}
Use this dependency when the app is in foreground- https://pub.dev/packages/flutter_local_notifications
Edit
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
//TODO
});
You are already using this code for when the app is in background. See that you are using onMessageOpenedApp. For firing the message when in foreground you can use onMessage. Example-
FirebaseMessaging.onMessage.listen((message) {
LocalNotificationService.display(message);
});
I am really wondering how to use the package https://pub.dev/packages/background_fetch.
I want to run a simple task to grab entries from my sqllite DB to change status.
On the main.dart file, I have :
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
initPlatformState();
await DotEnv().load('.env');
runApp(MyApp());
// Register to receive BackgroundFetch events after app is terminated.
// Requires {stopOnTerminate: false, enableHeadless: true}
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
BackgroundFetch.start();
}
First, I am really not if I have to put : BackgroundFetch.start(); ?
Then, here is my function called :
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
// Configure BackgroundFetch.
BackgroundFetch.configure(BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: false,
requiresBatteryNotLow: false,
requiresCharging: false,
requiresStorageNotLow: false,
requiresDeviceIdle: false,
requiredNetworkType: NetworkType.ANY
), (String taskId) async {
// This is the fetch-event callback.
print("[BackgroundFetch] Event received $taskId");
// take all photos not uploaded
final repo = di.get<DossierRepository>();
final photosNotUploaded = await repo.getPhotosNotUploaded();
await for (final photo in Stream.fromIterable(photosNotUploaded)) {
// resend them simply
repo.resendPhoto(photo.dossierId, photo.photoId);
}
// IMPORTANT: You must signal completion of your task or the OS can punish your app
// for taking too long in the background.
BackgroundFetch.finish(taskId);
}).then((int status) {
print('[BackgroundFetch] configure success: $status');
}).catchError((e) {
print('[BackgroundFetch] configure ERROR: $e');
});
}
I have this warning :
[TSBackgroundFetch start] Task flutter_background_fetch already registered
I understand I have to check if the task is not registered, but how ? And if I make changes to my code, I need to "destroy" the old task and register the new one ?
And is it the good way of using this plugin ? I have only 1 Task for my app, I want it to run all the time. Do I have to register Tasks ? Or this is enough ?
flutter_background_fetch is the default taskid of BackgroundFetch, which will be auto-registered by BackgroundFetch when initPlatformState. It means that you should not use flutter_background_fetch as a taskid.
I have an API that I'm checking, and when the response changes I need to send a notification to the user. I would like to know how to do this without FCM Push Notifications.
I'm using flutter-local-notifications and background fetch (https://github.com/transistorsoft/flutter_background_fetch) to do it. On the background fetch docs it says that background fetch will do your function once every 15 minutes, which is good enough for me.
This is my initPlatformState():
Future<void> initPlatformState() async {
// Load persisted fetch events from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
String json = prefs.getString(EVENTS_KEY);
if (json != null) {
setState(() {
_events = jsonDecode(json).cast<String>();
});
}
// Configure BackgroundFetch.
BackgroundFetch.configure(
BackgroundFetchConfig(
minimumFetchInterval: 15,
stopOnTerminate: false,
enableHeadless: true,
forceReload: true,
startOnBoot: true,
),
_onBackgroundFetch)
.then((int status) {
print('[BackgroundFetch] SUCCESS: $status');
setState(() {
_status = status;
});
}).catchError((e) {
print('[BackgroundFetch] ERROR: $e');
setState(() {
_status = e;
});
});
// Optionally query the current BackgroundFetch status.
int status = await BackgroundFetch.status;
setState(() {
_status = status;
});
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
}
I'm assuming that what's in the function that gets called in the fetch isn't needed for the question.
I tried this on my phone and simulator, and I used Xcode -> Simulate Background Fetch and it ran properly. It also ran properly when I opened the app. Unfortunately, it didn't run after 15 minutes. Is there something I'm missing?
How would I change my code to make the background fetch to happen every 15 minutes?
Yes, I spent lot of time trying to solve this but was not successful so I switched, I guessed what you are trying to do is to handle your own notification without Firebase or from an API like me, well this is it, after my search I was able to do this with the help of work manager package. So easy to use and implement, try it.
https://pub.dev/packages/workmanager