Handle terminated state using flutter local notifications & FCM - flutter

I'm created a handler for incoming FCM using flutter local notifications & on tap on each notification it can navigate to a specific screen and that working properly as soon as the app in the foreground and background but when the app terminated it navigates me to the home screen instead.
I use FirebaseMessaging.onBackgroundMessage() as a listener in case of background & terminated & FirebaseMessaging.onMessage.listen() in case of background with flutterLocalNotificationsPlugin.show() in both cases

When app is closed, you need to check for the notification that was clicked when the app was opened.
use:
final RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
to get the initial message somewhere while initializing your app and handle it if it is not null
Sometimes you might have to add a short delay to handle it after the rest of your app has been initialized. You can use
Future.delayed(const Duration(milliseconds: 300)).then((value){
// handle it in here
)};
Don't forget to do this after you call
await Firebase.initializeApp();

Related

Flutter: How to get/write data from/to shared preferences from action button's click using Awesome notifications?

I am using Awesome notifications to display notifications in my app. In the notifications, I have an action button, and when pressing it, the app is supposed to read and write some simple data from/to the memory of the phone using shared preferences. This is supposed to happen in the background without opening the app in the foreground.
I tried the following code:
#pragma("vm:entry-point")
static Future<void> onActionReceivedMethod(ReceivedAction action) async {
print('It works');
print(action.toMap());
final SharedPreferences prefs = await SharedPreferences.getInstance();
List<PinnedFolder> pinnedList = [];
try {
final String? pinnedString = prefs.getString('pinnedKey');
if (pinnedString != null) {
pinnedList = PinnedFolder.decode(pinnedString);
print('PinnedList got from memory, length: ${pinnedList.first.pinnedList.length}');
}
} catch (error) {
debugPrint('Error: couldnt get pinned folders: $error');
}
The "It works" and 'action.toMap()' get printed, but I can't get data from shared preferences. Is it so, that I can't use added packages in #pragma("vm:entry-point") functions? What would be the best way to fix the code? The action doesn't need to happen right after the button press, it can also happen the next time when the app is in the foreground, but so that the button action information is still available.
I found a solution using the package flutter_secure_storage.
const storage = FlutterSecureStorage(); await storage.write(key: my_key, value: my_value);
It seems to be so that only a few packages work in #pragma("vm:entry-point") functions, but this is one of them. I implemented it so that every time the app opens, it gets and handles the data from the secure storage before the app is launched.

Not receiving firebase dynamic link

Currently I am doing a project that calls a secondary app and I launch the 2nd app through InAppWebView. While the 2nd app is facing the user, the inappwebview is still running and sends live update, which I am capturing to trigger events, for example like it will send payment complete, allowing me to trigger to go summary page.
The issue I am having is, I am unable to smoothly transit back to my app. So there are two ways that I found that allow me to go back to the app.
One, launch the URL using my dynamic link but this method I am unable to receive the link.
Second, to launch it through external application like chrome.
The second method works perfectly other than the fact that the screen jumps to chrome. And it continues from where I left off.
However, what I want is both, continue from where I left off and not have the jumping of the chrome. Or from what I have read the right way is to direct to the activity using intent filter. But the smooth method I am not receiving the links but the jump method I am receiving the links.
Please help T - T
if you need to add dynamic links with firebase you can find the setup right in the docs and with the help with firebase_dynamic_links you can handle where to go inside your app and also can execute parameters from the link in the function below you make a stream that listen to any dynamic link clicked while the app in the background or foreground , the PendingDynamicLinkData is when the app terminated. the business logic and how the app interacts ups to you.
static Future<void> initDynamicLink() async {
FirebaseDynamicLinks.instance.onLink.listen((dynamicLink) async {
if (dynamicLink.link != Uri.parse("uri")) {
log(dynamicLink.link.queryParameters.toString());
final Uri deepLink = dynamicLink.link;
// you should handle the navigation or you logic here
}
}).onError((error) {
debugPrint("error from dynamic link Stream : : :: ${error.toString()}");
});
final PendingDynamicLinkData? initialLink =
await FirebaseDynamicLinks.instance.getInitialLink();
try {
if (initialLink != null) {
log(initialLink.link.toString());
final Uri deepLink = dynamicLink.link;
// you should handle the navigation or you logic here
}
} catch (e) {
debugPrint('No deepLink found');
}
}
don't forget to call this function in the main like this
void main()async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
initDynamicLink();
}

Open a screen when firebase notification received in flutter while app is background or terminated

want to create a screen that launch when a firebase notification is received while the app is in the background or terminated in flutter (like package callKeep ).please anyone help me create that.
You just need to handle its background handler method it will works.You need to initialize this method in main() method.
Future<void> backgroundHandler(RemoteMessage message) async {
debugPrint(message.toString());
debugPrint(message.data.toString());
debugPrint(message.notification!.title);
}
FirebaseMessaging.onBackgroundMessage(backgroundHandler);

Detecting flutter app opened from a terminated state with FCM and flutter_local_notifications

I am working on a flutter app that uses FCM data messages sent through cloud functions. In my app I'm receiving that data messages and then displaying a local notification. I need to be able to tap the local notification and have it direct the user to the correct screen. My current solution works when the app is in the foreground, or background, but NOT terminated. When it's terminated tapping on the notification appears to just open the app. Is there a way to detect if the app just opened from a terminated state? Below is my current code for when the notification is tapped, and the comments show what I want it to do
Future onSelectNotification(String payload) async {
Map data = json.decode(payload);
print('onSelectNotification: $data');
// if app is in foreground or background do this
if (data.containsKey('conversation_with')) {
print('Notification was for a conversation');
String conversationID = data['conversation_with'];
print('Conversation with UID: $conversationID');
PublicAppUser conversationWith =
await FriendDatabase().getPublicAppUser(conversationID);
navigatorKey.currentState!.pushNamed(
ConversationPublicUserScreen.routeName,
arguments: conversationWith);
}
// if it just opened from a terminated state do this
// ...
}

Why GetIt Library Needs Re-Init While App In Background(Not terminated)?

I'm trying to handle push notification cases like receiving messages, tapping notification etc. The problem happens when calling registered modules from GetIt while app in background(not terminated)
The simple scenario is,
Open the app
Tap home button and push the app to background
Send push notification to device
Then below method, onBackgroundMessage is triggered
locator retrieves GeneralRepository module
Future<void> onBackgroundMessage(RemoteMessage message) async {
try {
await Firebase.initializeApp();
var repo = locator<GeneralRepository>(); ---> BOOM! THIS LINE WHERE THE EXCEPTION OCCURS
var user = await repo.getUser();
showNotification(message);
} catch (e) {}
}
And error logs,
Object/factory with type GeneralRepository is not registered inside GetIt.
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?)
I found this SO answer and it works. It's saying, just re-init locator modules again.
But why should we do this ? App is not terminated!