When I receive a notification when my app is killed, I want to navigate to a specific screen of my choice when I tap on the notification. I have added the code for the navigation but the app does not navigate to that screen. Instead, the app opens and navigates to the first screen.
Here's my code:
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
RemoteNotification notification = message.notification;
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channel.description,
icon: '#drawable/splash',
playSound: true
),
));
// This does not seem to work as I am not directed to OrdersScreen
navigatorKey.currentState
.push(MaterialPageRoute(builder: (_) => OrdersScreen()));
}
void initState(){
super.initState();
var initializationSettingsAndroid = AndroidInitializationSettings('#drawable/splash');
var initializationSettings = InitializationSettings(android:initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
if (message != null) {
navigatorKey.currentState
.push(MaterialPageRoute(builder: (_) => OrdersScreen()));
}
});
}
If you are using firebase_messaging version 10.0.0 or above, the below code should work.
You should call handlePushNotification() function in your first screen.
To save last foreground screen path, you can use shared_preferences or another solution.
This is a very basic example for your problem.
void handlePushNotification() {
//FOREGROUND MESSAGES
FirebaseMessaging.onMessage.listen((message) {
_showSnackBar(message: message);
});
//BACKGROUND MESSAGES - ONRESUME
FirebaseMessaging.onMessageOpenedApp.listen((message) {
//Detect your current screen if you wish when "onResume" called.
if( currentScreenPath == 'FirstScreen' ){
_navigateSpecificScreen();
} else {
debugPrint('Specific screen is already foreground!');
_showSnackBar(message: message);
}
});
//BACKGROUND MESSAGES - ONLAUNCH
if (_firebaseMessaging != null) {
_firebaseMessaging!.getInitialMessage().then((message) {
if (null != message) {
_navigateSpecificScreen();
}
});
}
}
If your only problem is navigate to a specific screen when your app killed and relaunch, you can ignore the following.
If your application is more extensive, you should prefer to control all your navigations from a single class.
Maybe you can use onGenerateRoute property in MaterialApp.
This is an example: how to use onGenerateRoute
You can create a function for track the which screen is foreground in your RouteGenerator class.
You also need to handle Navigation.of(context).pop().
You don't want to navigate it again when a screen is already foreground when onResume called.
for me, I am using awesome notification to do that. this is done inside the home screen.
I do not know if this is the best way, but it works for me perfectly.
You also can distinguish the buttons inside the notification(if there are) using receivedNotification.buttonKeyPressed.
if the user does not press the button (or there is no button), so there is no buttonkeypressed, and I handled this in my code.
awsome notifications link: https://pub.dev/packages/awesome_notifications
////////////////////////////////////////////////////////////////////////////
///////////////////////////////// HOME SCREEN //////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
try {
AwesomeNotifications().actionStream.listen((receivedNotification) {
// print(receivedNotification.toString());
// print(receivedNotification.buttonKeyPressed.toString());
print("I am listning");
////////////// REPLY button
if (receivedNotification.buttonKeyPressed == "REPLY") {
Navigator.of(context).pushNamed(
ReplayInterface.routeName,
);
}
///////////////// REJECT button
else if (receivedNotification.buttonKeyPressed == "REJECT") {
getReject();
Navigator.of(context).pushNamed(RegectInterface.routeName);
}
///////////////// BODY Notification
else if (receivedNotification.buttonKeyPressed != "REJECT" &&
receivedNotification.buttonKeyPressed != "REPLY") {
print(receivedNotification.buttonKeyPressed);
Navigator.of(context).pushNamed(Counter.routName);
}
///////////////// NOT NOTIFICATION
else {
return;
}
});
} catch (e) {
print("e is $e");
Navigator.of(context).pushReplacementNamed("/");
}
super.initState();
}
`
Related
in my app firebase push notification is implemented.
on click event works when app is terminated.
but how to achieve notification on click of redirection when app is open.
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
#override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
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",
});
I am trying to update the home of MaterialApp widget depending on whether the user has sign up or not.
Below is the code inside the state of ``MaterialApp```
String? _userUid;
#override
void initState() {
FirebaseAuth.instance.authStateChanges().listen((user) {
//print('changed');
print(user?.uid);
updateUser(user?.uid);
});
super.initState();
}
void updateUser(String? USER_UID) {
setState(() {
_userUid = USER_UID;
});
}
Below is the code for the home property
home: _userUid == null ? const Onbaording2() : const HomeScreen(),
Somewhere inside the widget tree
final user = await _firebaseAuth.createUserWithEmailAndPassword(
email: email!,
password: password!,
);
After running the code above, there is a user created in firebase but the screen does not change.
Also the signOut works perfectly by signing me out when I use firebaseAuth.instance.signOut();
even if you change your variable it will not be redirected to the home screen because materialapp is only called when you first load or restart the app so rather than adding this condtion in home page or other way can be
#override
void initState() {
FirebaseAuth.instance.authStateChanges().listen((user) {
//print('changed');
print(user?.uid);
if(user == null){ navigate to onboarding }
else{ navigate to home page }
});
super.initState();
}
I have custom queuing logic using MediaItem which works properly when the UI is active. I am stopping the audio playback using onTaskRemoved method.
#override
Future<void> onTaskRemoved() async {
//if (!AudioServiceBackground.state.playing) {
logger.v("Task Removed");
await stop();
//}
}
#override
Future<void> stop() async {
await _player.stop();
await playbackState.firstWhere(
(state) => state.processingState == AudioProcessingState.idle);
}
However after swiping the task, notification is still visible and when I click the notification to resume nothing happens and app is stuck in splash screen.
I am using the plugin flutter_local_notifications to show a user notifications on the app. I have managed to show the notifications on the app and the user can click on the notifications, the user will be redirected to see all pending items from the notification in a notification page. I have set the function to detect the onclick on initstate. The whole function and method to show notification I have implemented it on the homescreen.
#override
void initState() {
super.initState();
initPlatformState();
getNotifications();
NotificationApi.init(initScheduled: true);
init();
_configureSelectNotificationSubject(); //function to redirect to Notifications page
}
which goes to
void _configureSelectNotificationSubject() {
print("clicked is the notification");
NotificationApi.onNotifications.stream.listen((String? payload) async {
await Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => WANotifications(current: current)),
(Route<dynamic> route) =>
true);
});
}
The challenge am facing with this implementation is that when a user clicks to go to the home screen , the user gets redirected automatically to the notifications page from the home screen without his/her consent. The redirect should only occur when they click on the notifications.
How can I set the redirect to only occur when the user clicks the notification only and not when they click to go to home screen
You can achieve this with the help of didNotificationLaunchApp.
bool get didNotificationLaunchApp =>
notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
// Use here,
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload = notificationAppLaunchDetails!.payload;
initialRoute = SecondPage.routeName;
}
Pl check full example here : https://pub.dev/packages/flutter_local_notifications/example
For non main file :
#override
void initState() {
super.initState();
localNotification(); // call below localNotification() here.
}
localNotification() async {
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin!.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
// redirect to new screen if true.
}
}