21

Is it possible to navigate to a specified path when clicking background FCM notification?

I created a Top-Level function and add it to the navigator path but its not working, when clicking on a background notification, it just opens the app

I GUESS I FOUND AN ISSUE

Now, I changed fcm configuration from home page to splash screen. The foreground doesn't navigate to the page, I think its because the Splash Screen is no longer available. When I click on the notification message, it just opens the app.

FCM Configuration

onBackgroundMessage: backgroundMessageHandler

Top-Level function

Future<dynamic> backgroundMessageHandler(Map<String, dynamic> message) {
  if (message.containsKey('data')) {
    getIt<NavigationService>().navigateTo('/${message['data']['screen']}');
  }
}

Payload

const payload: admin.messaging.MessagingPayload = {
                notification:{
                    title: `New Enquiry`,
                    body:`${customerName} published to ${subName}`,
                    badge: '1',
                    sound: 'default'
                },
                data: {
                    click_action: `FLUTTER_NOTIFICATION_CLICK`,
                    sound: `default`,
                    status: `chat`,
                    screen: `homePage`
                  }
            }

main.dart

GetIt getIt = GetIt.instance;

void main() {
  setupLocator();
    runApp(MyApp());
}

MaterialApp

 return MaterialApp(
      navigatorKey: NavigationService().navigatorKey,
      onGenerateRoute: Router.generateRoute,
    );

NavigationService and setupLocator

class NavigationService {
  final GlobalKey<NavigatorState> navigatorKey =
      new GlobalKey<NavigatorState>();

  Future<dynamic> navigateTo(String routeName) {
    return navigatorKey.currentState.pushNamed(routeName);
  }
}

void setupLocator() {
  getIt.registerLazySingleton(() => NavigationService());
}
CoderUni
  • 5,474
  • 7
  • 26
  • 58
BIS Tech
  • 17,000
  • 12
  • 99
  • 148

5 Answers5

9

You can use this function to navigation when the app is killed and receive notifications in the background

  FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage message) {
      print("FirebaseMessaging.getInitialMessage");
      if (message != null) {

        Navigator.of(context).pushNamed('/call');
      }
    });

This function only run once when the app open and get the last message.

You can read more at this doc: https://github.com/FirebaseExtended/flutterfire/blob/62e09975f9b3d14141585e62ddab6b98e667b7cf/docs/messaging/notifications.mdx

Trong Luong
  • 146
  • 2
  • 4
5

To handle notifications coming from a background state you can use the stream exposed by the FirebaseMessaging library.

FirebaseMessaging.onMessageOpenedApp.listen((remoteMessage) {
  // Handle navigation or perform any logic here
});
Santiago Racca
  • 128
  • 2
  • 7
  • 1
    The OP was asking for a method to handle Push Notifications in BACKGROUND mode, and so the answer is correct. Your answer uses a method to handle push notifications from a TERMINATED state, which works but it is not what the OP wanted. You should read the question and comments more carefully – Santiago Racca Aug 12 '21 at 12:41
  • This is the perfect solution I expected. I used onResume 1yr ago! onMessageOpenedApp works perfectly. Thanks @Santiago Racca – Rajesh Jan 25 '22 at 18:39
  • 1
    Glad I could help! – Santiago Racca Jan 26 '22 at 15:18
  • 1
    in my case FirebaseMessaging.onMessageOpenedApp and FirebaseMessaging.instance .getInitialMessage() methods are not fired on background notification click. – Muhammad Umair Saqib Mar 14 '22 at 07:03
0

In that example I used shared preference data like class id to check notification data contain this class id then it can navigate to a particular screen

void registerNotification() {
_firebaseMessaging.requestNotificationPermissions(
  const IosNotificationSettings(sound: true, badge: true, alert: true),
);

_firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
  print('onMessage: $message');
 // print(message['data']['classID']+" onMessage");
  if(SharedClassID.toString().contains(message['data']['classID']))
  {
   
    Navigator.push(context, MaterialPageRoute(builder: 
 (context)=>BroadcastNotification()));
    print("found");
  }
  
  else if(SharedClassID.toString().contains(message['data']['classID']) )
  {
   
    Navigator.push(context, MaterialPageRoute(builder: (context)=>MuseGalaryNew()));
    print("found");
  }
  else
  {
    print("not found");
  }

  return;
}, onResume: (Map<String, dynamic> message) {
  if(SharedClassID.toString().contains(message['data']['classID']))
  {
   
    Navigator.push(context, MaterialPageRoute(builder: (context)=>BroadcastNotification()));
    print("found");
  }
  else if(SharedClassID.toString().contains(message['data']['classID']))
  {
 
    Navigator.push(context, MaterialPageRoute(builder: (context)=>StudentHomework()));
    print("found");
  }

  else
    {
      print("not found");
    }


  //print(message['data']['classID']+" onResume");
  print('onResume: $message');
  return;
}, onLaunch: (Map<String, dynamic> message) {
  if(SharedClassID.toString().contains(message['data']['classID'])  )
  {
            Navigator.push(context, MaterialPageRoute(builder: (context)=>BroadcastNotification()));
    print("found");
  }
  else if(SharedClassID.toString().contains(message['data']['classID']))
  {
          Navigator.push(context, MaterialPageRoute(builder: (context)=>StudentHomework()));
    print("found");
  }

  else
  {
    print("not found");
  }
  return;
});

}
Ichigo Kurosaki
  • 3,765
  • 8
  • 41
  • 56
Mr vd
  • 878
  • 1
  • 10
  • 19
  • Regardless of the screen that you want to open, what is required is to know how to open the screen by Navigator in case the application is utterly closed, thank you – farouk osama Sep 26 '20 at 12:19
  • you need to subscribe event by using _firebaseMessaging.subscribeToTopic("Your topic name")..after receiving notification when the app is closed and it will redirect to that specific page when you click on notification – Mr vd Sep 27 '20 at 06:02
0

I hope you have configured the required native side declaration required by firebase_messaging specfied here

Based on the payload you have given :

Payload

const payload: admin.messaging.MessagingPayload = {
                notification:{
                    title: `New Enquiry`,
                    body:`${customerName} published to ${subName}`,
                    badge: '1',
                    sound: 'default'
                },
                data: {
                    click_action: `FLUTTER_NOTIFICATION_CLICK`,
                    sound: `default`,
                    status: `chat`,
                    screen: `homePage`
                  }
            }

Have your onResume like this, similarly same for onLaunch

onResume :

onResume: (Map<String, dynamic> message) {
  if(SharedClassID.toString().contains(message['data']['classID']))
  {
   
    Navigator.push(context, MaterialPageRoute(builder: (context)=>BroadcastNotification()));
    print("found");
  }
  else if(SharedClassID.toString().contains(message['data']['classID']))
  {
 
    Navigator.push(context, MaterialPageRoute(builder: (context)=>StudentHomework()));
    print("found");
  }

  else
    {
      print("not found");
    }


  //print(message['data']['classID']+" onResume");
  print('onResume: $message');
  return;
}

I can find out the reason why it is not working because in the payload the key is screen:'homePage' and i can notice that you are not passing classID so what is happening is it failing to evaluate into the if else condition you have added.

You update the if/else condition like below :

if(message['data']['screen'] == 'homePage')
  {
    //TODO: route
  
  }else if(message['data']['screen'] == 'homeWorkPage'){
    //TODO: route
  }

If you want to route based on the SharedClassId then pass the classId in the payload and route. example snippet :

if(message['data']['classId'] == SharedClassId)
  {
    //TODO: route
  
  }else if(message['data']['classId'] == SharedClassId){
    //TODO: route
  }
Akshay Kumar U
  • 406
  • 3
  • 10
0

Latest Code as of now AUG 2021. First of all your project dart version and firebase plugin version are important things to integrate firebase notification. If you are using a lower version, then you may face some issues. For my flutter project, I am using the latest null safe Firebase messaging plugin. If you use Flutter messaging 10.0.4 or higher version then you don't need to add any metadata on the android manifest file. You can see the complete code here.

  firebase_messaging: ^10.0.4
  firebase_core: ^1.4.0
  flutter_local_notifications: ^8.1.1

if the flutter app is closed then the below code will be called. It will be called only once & you should provide click_action as "FLUTTER_NOTIFICATION_CLICK" as custom data on the firebase console cloud messaging window.

  // when app is closed and it will be called only once
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
 print("fireabse closed app msg");

  if (message != null) {
    print( " firebase msg received closed add "+message.data.toString());
    Navigator.push(context, MaterialPageRoute(builder: (context){ return product(prodid: message.data["prodid"]);}));

    /*  Navigator.pushNamed(context, '/message',
        arguments: MessageArguments(message, true));
  */

  }
});

To display the notification icon on the status bar uses the below code.

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;
      print(" firebase msg - "+message.data.length.toString());
      if (notification != null && android != null) {
        print("firebase msg body "+ notification.body.toString());
        flutterLocalNotificationsPlugin.show(
            notification.hashCode,
            notification.title,
            notification.body,
            NotificationDetails(
              android: AndroidNotificationDetails(
                channel.id,
                channel.name,
                channel.description,
                // TODO add a proper drawable resource to android, for now using
                //      one that already exists in example app.
                icon: 'noti_icon',
                color: Colors.red

              ),
            ));


      }
    });
Kamal Bunkar
  • 1,354
  • 1
  • 16
  • 20
  • You are answering for on killed & onMessage, but the problem is onBackground. Ie the app is minimized by using Home Button, and unable to use any navigation process. Before 2 years it was simple to use "onResume" – Rajesh Jan 25 '22 at 18:30