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

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

Related

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

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

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.

Can I hide Android Notification bar if I used Audio Service in Flutter?

I used the latest Dart package of audio_service (0.18.4). My use case is different. I have main menu on the first screen and another sub-menu to play music in the other screen. I just want to hide the Notification bar after user click on close button to music player. Close here means that user stops playing music and goes directly to main menu.
I started and used get_it package as singleton to run AudioPlayerHandler class in all screens.
import 'package:audio_player_final/audio/audio_handler.dart';
import 'package:get_it/get_it.dart';
GetIt getThem = GetIt.instance;
Future<void> setup() async {
getThem.registerSingleton<AudioPlayerHandler>(await initAudio(), dispose: (audio) => audio.customAction("dispose"));
}
This is the code if user click close button in Music Player:
Future<void> customAction(String name, [Map<String, dynamic>? extras]) async {
if (name == "close") {
await stop();
await setShuffleMode(AudioServiceShuffleMode.none);
await setRepeatMode(AudioServiceRepeatMode.none);
playbackState.add(playbackState.value.copyWith(
repeatMode: AudioServiceRepeatMode.none,
shuffleMode: AudioServiceShuffleMode.none,
));
if (_playlist.children.isNotEmpty) {
for (int i = _playlist.children.length - 1; i >= 0; i--) {
await removeQueueItemAt(i);
}
dev.log("Cleaning previous playlist");
}
}
}
What I want is to hide Notification bar so user won't press it since there are no more queue of music inside it. I had removed them all in above function if user click close button. If user press the Playback button on Notification bar, the error will get displayed that the queue is empty. I don't want that to happen.
If I use dispose function, the Notification will be disappeared, but audio_service is not meant to restart the init function. So it will be impossible to run the music player again if user want to play music again as it has been disposed.
Future<void> customAction(String name, [Map<String, dynamic>? extras]) async {
if (name == "dispose") {
await stop();
await _player.dispose();
}
}
Is there anyway to hide the Notification bar of Music Player from audio_service?
Any tips and trick will be welcomed.
Thanks

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