How To Use GetX Translations On Notifications(On Terminated State)? - flutter

GetX translation is not working on notifications that are created when the app is in the terminated state.
This is how I show the notifications. Below translations work in foreground notifications but not in the background (terminated)
showNotification(message){
...
var title = 'some_title'.tr
var body = 'some_body'.tr
...
}
And rest of the code, FirebaseMessaging.onBackgroundMessage(onBackgroundMessage) and onBackgroundMessage defined as required
Future<void> onBackgroundMessage(RemoteMessage message) async {
showNotification(message);
return Future.value();
}
void main() async {
...
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
runApp(const MyApp());
}
Note: I know, this issue is happening because the app is not running yet. GetMaterialApp did not run, as a result, GetX didn't work too
Note: I'm not having any issue with navigations or showing notifications.

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",
});

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);
}

Flutter Background messages internationalization

In my flutter app, i want to translate some text in the push notification body.
For foreground notifications. there is no problem.
For backgroud notifiactions i use :
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
And _firebaseMessagingBackgroundHandler must be a top-level function.
So, how can i use my famous AppLocalizations.of(context).cancel, since I don't have a context here ?
Since _firebaseMessagingBackgroundHandler is a top-level function- you can pass context as an optional parameter. So that you can access the context.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message,
{BuildContext? context}){
//Your code
}
In MyApp create variable static
Exp:
static BuildContext? mContext;
in build function of MyApp
mContext = context;

Flutter Riverpod: Refresh Page Offscreen using State Notifier

I'm using StateNotifier and Riverpod.
I have a notification page, which contains a list of notification. When a notification arrives, I trigger a refresh for notification. However, when I navigate to notification page, it still using the old (cached?) list.
How do I refresh a page offscreen?
Foreground Message
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_localNotification.showLocalNotification(message);
ProviderContainer().read(notificationProvider).getNotification();
});
Background Message
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
final LocalNotification localNotification = LocalNotification();
await localNotification.init();
localNotification.showLocalNotification(message);
ProviderContainer().read(notificationProvider).getNotification();
}
NotificationProvider
Future<void> getNotification() async {
try {
state = const NotificationLoadingState();
_notificationList = await _notificationRepository.getNotification();
state = NotificationLoadedState(_notificationList); // ==> I get a new list here
} catch (e, s) {
state = const NotificationErrorState('error fetching notification');
}
}
UI
final state = watch(notificationProvider.state);
if (state is NotificationLoadingState) {
return _buildLoading();
} else if (state is NotificationLoadedState) {
return _buildLoaded(state.notificationList); // ==> I still get the old list here
} else if (state is NotificationErrorState) {
return _buildError(state.message);
}
Edit:
I managed to solve the foreground message handler by using navigatorKey.currentContext.
However, I still haven't solved background message handler.
I've tried changing my main.dart to use UncontrolledProviderScope with a global ProviderContainer, which then was called from background message handler. It's still not refreshing.
With this line you are creating a new instance for your providers :
ProviderContainer().read(notificationProvider).getNotification();
you need to use a context to get the existing instance of ProviderContainer:
context.read(notificationProvider).getNotification();
or if you are not inside ui make dependencies between your providers