Cannot find AwesomeNotifications().actionStream - flutter

I would like to add a button to my flutter notification. The button is shown but I can't catch the action.
AwesomeNotifications().createNotification(
content: NotificationContent(
id: 888,
channelKey: 'NetVideo',
title: 'NetVideo',
body: 'Server in ascolto'),
actionButtons: <NotificationActionButton>[
NotificationActionButton(key: 'yes', label: 'Yes'),
],
);
AwesomeNotifications().actionStream still exists?

According to documentation:
All streams (createdStream, displayedStream, actionStream and dismissedStream) was replaced by global static methods. You must replace your old stream methods by static and global methods, in other words, they must be static Future and use async/await and you MUST use #pragma("vm:entry-point") to preserve dart addressing.
In other words you should create a notification listener
class NotificationController {
/// Use this method to detect when a new notification or a schedule is created
#pragma("vm:entry-point")
static Future <void> onNotificationCreatedMethod(ReceivedNotification receivedNotification) async {
// Your code goes here
}
/// Use this method to detect every time that a new notification is displayed
#pragma("vm:entry-point")
static Future <void> onNotificationDisplayedMethod(ReceivedNotification receivedNotification) async {
// Your code goes here
}
/// Use this method to detect if the user dismissed a notification
#pragma("vm:entry-point")
static Future <void> onDismissActionReceivedMethod(ReceivedAction receivedAction) async {
// Your code goes here
}
/// Use this method to detect when the user taps on a notification or action button
#pragma("vm:entry-point")
static Future <void> onActionReceivedMethod(ReceivedAction receivedAction) async {
// Your code goes here
// Navigate into pages, avoiding to open the notification details page over another details page already opened
MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil('/notification-page',
(route) => (route.settings.name != '/notification-page') || route.isFirst,
arguments: receivedAction);
}
}
And register it inside the library
AwesomeNotifications().setListeners(
onActionReceivedMethod: (ReceivedAction receivedAction){
NotificationController.onActionReceivedMethod(context, receivedAction);
},
onNotificationCreatedMethod: (ReceivedNotification receivedNotification){
NotificationController.onNotificationCreatedMethod(context, receivedNotification);
},
onNotificationDisplayedMethod: (ReceivedNotification receivedNotification){
NotificationController.onNotificationDisplayedMethod(context, receivedNotification);
},
onDismissActionReceivedMethod: (ReceivedAction receivedAction){
NotificationController.onDismissActionReceivedMethod(context, receivedAction);
},
);

Related

Cannot navigate to a specific page when tapping a notification (when app is in background)

I am developing an app in flutter. I used awesome_notification package to show notifications. In awesome_notification, onActionReceivedMethod works fine when an app is in the foreground but does not work when an app is in the background. How to handle this?
#pragma("vm:entry-point")
static Future <void> onActionReceivedMethod(ReceivedAction receivedAction) async {
// code to navigate some page
} // not calling when user tap notification
Also, onMessageOpenedApp function is not triggered when tapping the notification. How to solve this? Kindly help me to resolve this.
FirebaseMessaging.onMessageOpenedApp.listen((message) async {
// code to navigate some page
}); // not calling when user tap notification
I encountered the same problem with local_notification, to pass an information from the vm entry point to the app.
I solved it by doing an isolate sent / receive like this:
//needed for isolate
import 'dart:isolate';
import 'dart:ui';
//outside main
const String channel = 'channel_key';
#pragma('vm:entry-point')
void onReceiveBackgroundResponse(NotificationResponse notificationResponse) async {
final sendPort = IsolateNameServer.lookupPortByName(channel);
sendPort?.send(notificationResponse.actionId);
}
...
//inside main
void listenNotification() {
final receivePort = ReceivePort();
IsolateNameServer.registerPortWithName(receivePort.sendPort, channel);
receivePort.asBroadcastStream().listen((event) {
print(event);
});
}

Flutter Firebase messaging: sending a user to a specific screen

Im trying to work with firebase messaging on flutter, and here's the situation. I have push notification implemented simply with the following:
void main() async {
WidgetsFlutterBinding();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// await _configureFirebaseAuth();
// await _configureFirebaseStorage();
// _configureFirebaseFirestore();
runApp(const MyApp());
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
print("Handling a background message: ${message.messageId}");
}
This allows the push notifications to appear, but there is no real functionality to it. In my case, i would like to call navigator and push a certain route. Since this requires context, I placed the following method in my home page(the first screen actually built in the app):
#override
void initState() {
// TODO: implement initState
handleMessageOnBackground();
super.initState();
}
void handleMessageOnBackground() {
FirebaseMessaging.instance.getInitialMessage().then(
(remoteMessage) {
if (remoteMessage != null) {
print('Message: ${remoteMessage.data}');
RemoteMessage? message = remoteMessage;
}
},
);
}
The idea is that, in the initstate method, i now have context, so I would be able to Navigate to a different page. While this works(to my understanding) when the app is completely closed, it will not cover the cases when remote notifications are caught by the .onMessage or the .onBackgroundMessage. Is there any way to pass NotificationMessages from the top of my app down into the main page to use for navigation?
Thank you!
If you want to navigate without context use a GlobalKey.
Define a Globalkey //not inside any class
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
and inside your MaterialAppWidget()
MaterialApp(
navigatorKey: navigatorKey,
...
);
From firebase you'll receive some data in _firebaseMessagingBackgroundHandler(){}
based on that data you can navigate to any screen.
//if you are creating a notification using that data then put required data in payload of that notification. so you can receive that payload when you tap on that notification that will help you to navigate to a specific screen.
so navigate without context like this.
navigatorKey.currentState
?.pushNamed(AnotherScreen.routeName);
if you want to pass arguments
navigatorKey.currentState
?.pushNamed(IOSNotificationIntentScreen.routeName, arguments: {
"arg1": "hello arg 1",
"arg2": "hello arg 2",
});

The method 'onSelectNotifications' isn't defined using flutter local notification plugin

in flutter_local_notification 12.0.2
The onSelectNotification parameter is not anymore.
So what is the alternative of this. So that we can handle callback when we click on notification.
you can stream the notification
import 'package:rxdart/rxdart.dart';
...
// use this
class MyNotifManager {
static final onNotifications = BehaviorSubject<String?>();
...
// then add the payload to your local notification
// exampel for the foreground notif
onDidReceiveNotificationResponse: (payload) async {
onNotifications.add(payload.payload); // add payload to the stream
},
then to handle the callback:
Future<void> listenNotification() async =>
MyNotifManager.onNotifications.stream.listen(onClickNotification);
and for your action after click
void onClickNotification(String? payload) {
Navigator.push();
}
call the stream on your initState
#override
void initState() {
super.initState();
MyNotifManager.init();
listenNotification();
}
with this method, you will able to handle calback when click on notification.
It has onDidReceiveNotificationResponse callback when plugin initialize.
_plugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (details) {
// Click Notification Event Here
},
);
If you click notification when app is terminated,
_plugin.getNotificationAppLaunchDetails().then((value) {
// Click Notification Event Here
});

How to implement a communication chain in Flutter app with non-Flutter component

I'm trying to implement a communication chain in my app.
The app has at least to layer:
core, which is responsible for network communications. It's implemented as a Dart library
UI, which is responsible for communication with user. It's implemented as a Flutter app.
The core has a piece that handles invitations. Communication part is asynchronous and works in a way:
receive a request
handle request
send a response
void _handleMemberInviteRequest(AtNotification notification) async {
final sender = AtSignMember(atSign: notification.from);
if (await onMemberInvitation(sender)) {
send(notification.from, CommunicationVerbs.memberInviteRespond.name,
"accept");
} else {
send(notification.from, CommunicationVerbs.memberInviteRespond.name,
'reject');
}
}
onMemberInvitation is an event handler that in my understanding should be implemented in Flutter app. My problem is that I need user to accept an invitation. The whole chain of actions I see:
Request is received (core) -> onMemberInvitation is invoked (core) -> Confirmation dialog pops up (Flutter app) -> Response is returned by onMemberInvitation (core) -> Response is sent (core).
What I can't figure out is how to make Flutter to pop up the confirmation and answer with the result. I use BLoC patter for state management. So I though of having a separate Cubit that would emit a state that would be listened by a BlocListener on a top of application and pop up the dialog.
class Confirmation extends Cubit {
void askForConfirmation(sender) {
emit(ConfirmationState("InvitationConfirmation"));
}
void gotConfirmation(bool confirmation) {
emit(ConfirmationResponseState(confirmation));
}
}
and in app initialization implement an onMemberInvitation handler:
Future<bool> onMemberInvitation(sender) async {
askForConfirmation(sender);
await for (/* here somehow wait for `ConfirmationResponseState`*/) {
return confirmation;
}
}
But then I can't realise how do I wait for the response in onMemberInvitation handler.
Any ideas? Can BLoC be utilised here as well? Or because it's outside of Flutter app some custom streams have to be implemented? Or there is another way?
What you need is an async onMemberInvitation function that you can finish from outside the scope of the function itself.
You can achieve this using a Completer. This enables you to emit the result of the confirmation from anywhere while pausing the execution of onMemberInvitation until the result arrived. Check the sample below.
import 'dart:async';
Completer completer = new Completer<bool>();
void main() async {
String sender = 'test';
completer = new Completer();
if (await onMemberInvitation(sender)) {
print("accept");
} else {
print('reject');
}
}
Future<bool> onMemberInvitation(String sender) async {
askForConfirmation(sender);
print('UI event emitted');
return await completer.future;
}
void askForConfirmation(String sender) async {
// TODO: emit the state to the UI here
await Future.delayed(Duration(seconds: 3));
//TODO: call this when you get the confirmation event
gotConfirmation(true);
}
void gotConfirmation(bool confirmation) {
completer.complete(confirmation);
}

Retry Future<void> API call on timeout

I've implemented on TimeoutException on each API calls. Initially, it was just showing a simple bottomsheet dialog that is being called from a Utils class which is accessible globally so that I don't have to paste the Widget in each API call and just call the function in the Utils class with a String message parameter.
Like:
on TimeoutException {
Utils().showError("login timeout");
}
Now to improve the UX, I added a "retry" button inside the bottomsheet dialog for the purpose of retrying/recalling the function.
I've created dynamic widgets that is reusable across the project and receives a Function and it works.
However, my issue here is how can I pass Future<void> functionName to the Utils widget so that it can be used in other API calls?
What I tried:
static Future<void> userLogin(BuildContext context, String email,
String password, bool rememberMe) async {
// does something
} on TimeoutException {
Utils().showTimeoutDialog(context, userLogin);
then in my Utils class:
showTimeoutDialog(BuildContext context, Function callback) {
onPress: () async { await callback;}
it works but the callback is being called immediately without even pressing the button.