how to open particular screen on clicking on push notification for flutter

111,852

Solution 1

A few things here:

1- click_action has to be set to "FLUTTER_NOTIFICATION_CLICK"

2- click_action has to be set in the data section of a payload

DATA='{
  "notification": {
    "body": "this is a body",
    "title": "this is a title",
  },
  "data": {
    "click_action": "FLUTTER_NOTIFICATION_CLICK",
    "sound": "default", 
    "status": "done",
    "screen": "screenA",
  },
  "to": "<FCM TOKEN>"
}'

This should allow you to receive the message in the onMessage handler in your flutter app.

From there you can call Navigator.of(context).pushNamed(message['screen']).

If you don't have a BuildContext at that point, you can register a GlobalKey as the navigatorKey property of your MaterialApp, and use it to access your Navigator globally, via GlobalKey.currentState

Solution 2

As @xqwzts method works well for receiving messages on App is an open state,

the following example will navigate to a specific page,

[THE CODE IS TAKEN FROM THE FIREBASE MESSAGING PLUGIN EXAMPLE CODE ONLY AND IT NAVIGATES TO A NAMED PAGE, IN WHICH THE DATA WE SENT VIA FIREBASE CONSOLE]

//eg: if you give /Nexpage3  in the status field then it will navigate to Nextpage3 of your App

enter image description here

UNDERSTAND THE 2 THINGS,FCM NOTIFICATIONS HAS 2 SECTIONS

1st Message Title Section in your firebase cloud messaging page is called Notification Data[when the App is minimized or closed it will be shown as a notification]

2nd Message Title section which is in the bottom of the webpage is called Message Data, [it will be shown In inside app as a notification or an Alert dialogue that's up to your wish]

STEPS Create a dummy Project then use firebase message plugin, and in that Box give BMW Cars as atopic and click subscribe

Now go to your console then send a message with the FOLLOWING FORMAT it must contain Id and Status keys because we are parsing the Id and Status Keys in order to show NextPage with the Status Key's Value but if u prefer a field like title or body then u can do that too but make sure to parse the map value in ur flutter code.

//THIS IS A LITTLE BIT MODIFIED VERSION OF Example Code given in Firebase 
//Messaging Plugin
//WHEN U PASTE THE CODE IN UR VS CODE OR ANDROID STUDIO PLEASE Format the 
//Document because it is aligned in single lines

import 'dart:async';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(
    new MaterialApp(
      home: new PushMessagingExample(),
      routes: <String,WidgetBuilder>{
        "/Nexpage1":(BuildContext context)=> new Nexpage1(),
        "/Nexpage2":(BuildContext context)=> new Nexpage2(),
        "/Nexpage3":(BuildContext context)=> new Nexpage3(),
        } ),);}


//INITIAL PARAMETERS
String _homeScreenText = "Waiting for token...";
bool _topicButtonsDisabled = false;
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
final TextEditingController _topicController = new TextEditingController(text: 'topic');
final Map<String, Item> _items = <String, Item>{};
Item _itemForMessage(Map<String, dynamic> message) {
  final String itemId = message['id'];
  final Item item = _items.putIfAbsent(itemId, () => new Item(itemId: itemId))..status = message['status'];
      return item;
}

//MAIN CLASS WHICH IS THE HOMEPAGE
class PushMessagingExample extends StatefulWidget {
  @override
  _PushMessagingExampleState createState() => new _PushMessagingExampleState();
}


class _PushMessagingExampleState extends State<PushMessagingExample> {
void _navigateToItemDetail(Map<String, dynamic> message) {
final String pagechooser= message['status'];
Navigator.pushNamed(context, pagechooser);
}

//CLEAR TOPIC
void _clearTopicText() {setState(() {_topicController.text = "";_topicButtonsDisabled = true;});}

//DIALOGUE
void _showItemDialog(Map<String, dynamic> message) {showDialog<bool>(context: context,builder: (_) => _buildDialog(context, _itemForMessage(message)),).then((bool shouldNavigate) {if (shouldNavigate == true) {_navigateToItemDetail(message);}});}

//WIDGET WHICH IS GOING TO BE CALLED IN THE ABOVE DIALOGUE
Widget _buildDialog(BuildContext context, Item item) {return new AlertDialog(content: new Text("Item ${item.itemId} has been updated"),actions: <Widget>[new FlatButton(child: const Text('CLOSE'),onPressed: () {Navigator.pop(context, false);},),new FlatButton(child: const Text('SHOW'),onPressed: () {Navigator.pop(context, true);},),]);}


@override
void initState() {
super.initState();
_firebaseMessaging.configure(
onLaunch: (Map<String, dynamic> message) async { _navigateToItemDetail(message);},
onResume: (Map<String, dynamic> message) async { _navigateToItemDetail(message);},
onMessage: (Map<String, dynamic> message) async {_showItemDialog(message);},);

//GETTING TOKEN FOR TESTING MANUALY
_firebaseMessaging.getToken().then((String token) {assert(token != null);setState(() {_homeScreenText = "Push Messaging token: $token";});print(_homeScreenText);});}



  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(  title: const Text('Push Messaging Demo'),),
        body: new Material(
          child: new Column(
            children: <Widget>[
              new Center(
                child: new Text(_homeScreenText),
              ),
              new Row(children: <Widget>[
                new Expanded(
                  child: new TextField(
                      controller: _topicController,
                      onChanged: (String v) {
                        setState(() {
                          _topicButtonsDisabled = v.isEmpty;
                        });
                      }),
                ),
                new FlatButton(
                  child: const Text("subscribe"),
                  onPressed: _topicButtonsDisabled
                      ? null
                      : () {
                          _firebaseMessaging
                              .subscribeToTopic(_topicController.text);
                          _clearTopicText();
                        },
                ),
new FlatButton(child: const Text("unsubscribe"),
onPressed: _topicButtonsDisabled? null: () { _firebaseMessaging.unsubscribeFromTopic(_topicController.text);
 _clearTopicText();},),

])],),));}}




//THREE DUMMY CLASSES FOR TESTING PURPOSE 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//PAGE1
class Nexpage1 extends StatefulWidget {  @override  _Nexpage1State createState() => _Nexpage1State();}
class _Nexpage1State extends State<Nexpage1> { @override Widget build(BuildContext context) { return Scaffold(body: new Center(child: new Text(" Page1"),));}}

//PAGE2
class Nexpage2 extends StatefulWidget {  @override  _Nexpage2State createState() => _Nexpage2State();}
class _Nexpage2State extends State<Nexpage2> {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(child: new Text("2pending"),)      );  }}

//PAGE3
class Nexpage3 extends StatefulWidget {  @override  _Nexpage3State createState() => _Nexpage3State();}
class _Nexpage3State extends State<Nexpage3> {  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(child: new Text("3connected"),)      );  }}


//THIS IS THE CLASS WHICH IS USED TO PARSE THE INFORMATION
class Item {
  Item({this.itemId});
  final String itemId;
  StreamController<Item> _controller = new StreamController<Item>.broadcast();
  Stream<Item> get onChanged => _controller.stream;
  String _status;
  String get status => _status;
  set status(String value) {
    _status = value;
    _controller.add(this);
}

  static final Map<String, Route<Null>> routes = <String, Route<Null>>{};
  Route<Null> get route {
    final String routeName = '/detail/$itemId';
    return routes.putIfAbsent(
      routeName,
      () => new MaterialPageRoute<Null>(
            settings: new RouteSettings(name: routeName),
            builder: (BuildContext context) => new Nexpage3(),
          ),
    );
  }
}

Solution 3

For anyone would like to migrate to the latest version of Firebase Messaging before Null Safety (work on iOS and Android), here are the steps

pubspec.yaml

firebase_core: ^0.7.0
firebase_messaging: ^8.0.0-dev.15

main.dart

GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

Future<void> main() async {
      
        WidgetsFlutterBinding.ensureInitialized();
        await Firebase.initializeApp();
        await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
          alert: true,
          badge: true,
          sound: true,
        );
        runApp(new MyApp());
}


class MyApp extends StatefulWidget {

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

@override
  Widget build(BuildContext context) {
  return MaterialApp(
              navigatorKey: navigatorKey,
              title: ...

   );
  }

}

homeScreen.dart

    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("onBackgroundMessage: $message");
    }
    
    class HomeScreen extends StatefulWidget {
      @override
      _HomeScreenState createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen>{
    
      @override
      void initState() {
        super.initState();
    
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
        print("onMessage: $message");
    });
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
        print("onMessageOpenedApp: $message");
            
                 
          if (message.data["navigation"] == "/your_route") {
            int _yourId = int.tryParse(message.data["id"]) ?? 0;
            Navigator.push(
                    navigatorKey.currentState.context,
                    MaterialPageRoute(
                        builder: (context) => YourScreen(
                              yourId:_yourId,
                            )));
        });
      }
    }

Noted that iOS notification will appear on top (head-up display) and trigger method of onMessage (when app in foreground) and onBackgroundMessage (when app in background or terminated).

For android notification will appear on tray at the top and trigger method of onBackgroundMessage (when app in background or terminated) ONLY. You have to use third party solution like flutter_local_notifications or overlay_support to display notification during onMessage (app in foreground).

For iOS, when notification is clicked (when app on background, terminated or foreground), method onMessageOpenedApp will be triggered. For Android, this scenario will work when app on background or terminated only (if head-up notification display/banner is enabled - you need to create channel_id for android)

You no longer need to send click_action: FLUTTER_CLICK_ACTION on data payload in order to have clickable event on notification. FirebaseMessaging will handle for you

Solution 4

To provide the payload use:

1- click_action has to be set to "FLUTTER_NOTIFICATION_CLICK" - In android manifest file, we have defined the intent for it.

2- click_action has to be set in the data section of a payload

Define a global variable for NavigatorState:

import 'package:flutter/cupertino.dart';

/// Global variables
/// * [GlobalKey<NavigatorState>]
class GlobalVariable {
  
  /// This global key is used in material app for navigation through firebase notifications.
  /// [navState] usage can be found in [notification_notifier.dart] file.
  static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}

Go to your MaterialApp and add

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ScrollConfiguration(
      behavior: BounceScrollBehavior(),
      child: MaterialApp(
        navigatorKey: GlobalVariable.navState,
        debugShowCheckedModeBanner: false,
        theme: themeData,
        home: App(),
      ),
    );
  }
}

When you press on the notification in app bar, it calls onResume. You can navigate to the desired page as follows.

void listenToNotification() {
    fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        getPreviousNotifications();
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: ${message["data"]}");
        SchedulerBinding.instance.addPostFrameCallback((_) {
          Navigator.of(GlobalVariable.navState.currentContext)
              .push(MaterialPageRoute(
                  builder: (context) => TimelineView(
                        campaignId: message["data"]["campaign"],
                      )));
        });
      },
    );
  }

Solution 5

Step 1: pass one key-value pair in the firebase notification as click_action: FLUTTER_CLICK_ACTION.

Step 2: Using step 1 you'll receive the onTap callback of notification inside onResume or onLaunch method.

Step 3: Perform the following scenario for navigating to a specific screen on click of notification.

  • When you build your MaterialApp, pass a navigatorKey parameter which specifies the key to use for the navigator and then assign that key to your material app as shown below:
class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey<NavigatorState> navigatorKey = GlobalKey(debugLabel: "Main Navigator");

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      navigatorKey: navigatorKey,
      home: new Scaffold(
        appBar: AppBar(),
        body: new Container(),
      ),
    );
  }
}
  • Now, from the onResume or onLaunch method navigate to your screen using below line of code:
 navigatorKey.currentState.push(
    MaterialPageRoute(builder: (_) => Dashboard())
 );
Share:
111,852
siva kumar
Author by

siva kumar

Updated on February 18, 2022

Comments

  • siva kumar
    siva kumar about 2 years

    I am trying to achieve open specific screen on clicking push notification and my payload looks like this:

     var payload = {
            notification: {
                title: notificationTitle,
                body: notificationMessage,
                click_action:"/screena",sound:"default",
            }
        };
    

    I am getting notification but I am unable to catch notification click event in flutter how to catch it. I am using flutter messaging

    https://github.com/flutter/plugins/tree/master/packages/firebase_messaging

    and my firebase push message service code looks like this

     pushMessagingService() async{
    messagingreference.configure(
    onMessage: (Map<String, dynamic> message) {
    
      print("I am here in on message");
      print(message);
    },
    onLaunch: (Map<String, dynamic> message) {
      print("I am here onLaunch");
      print(message);
    },
    onResume: (Map<String, dynamic> message) {
      print("I am hereonResume");
      print(message);
    },
    );
      messagingreference.requestNotificationPermissions(
      const IosNotificationSettings(sound: true, badge: true, alert: true));
     messagingreference.onIosSettingsRegistered
      .listen((IosNotificationSettings settings) {
    print("Settings registered: $settings");
     });
     messagingreference.getToken().then((String token) async {
    
    
    print(token);
     });
     }
    

    here I can get the message as @xqwzts said in on message when my app is in the foreground but my question is how to catch click event from push notification raised in the system tray and navigate to the required screen.