Home » Android » android – how to open particular screen on clicking on push notification for flutter

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

Posted by: admin April 23, 2020 Leave a comment

Questions:

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.

How to&Answers:

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

Answer:

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

Answer:

Initially, @xqwzts answer not working for me. After a lot of research, I find that We need to initialize the configure method little delay. I attached the code below. It will helpful for others.

void initState() {
        // TODO: implement initState
        super.initState();
        _firebaseMsgListener();
      }

      void _firebaseMsgListener() {
        // if (Platform.isIOS) iOS_Permission();

        _firebaseMessaging.getToken().then((token) {
          Fimber.d("=====> token : $token");
          UpdateTokenRequest request = UpdateTokenRequest();
          request.token = token;
          homeBloc.updateFCMToken(request);
        });

        Future.delayed(Duration(seconds: 1), () {
          _firebaseMessaging.configure(
            onBackgroundMessage: myBackgroundMessageHandler,
            onMessage: (Map<String, dynamic> message) async {
              Fimber.d("=====>on message $message");
              Fluttertoast.showToast(msg: "onMessage $message");
            },
            onResume: (Map<String, dynamic> message) async {
              Fimber.d("=====>onResume $message");
              Fluttertoast.showToast(msg: "onResume $message");
            },
            onLaunch: (Map<String, dynamic> message) async {
              Fimber.d("=====>onLaunch $message");
              Fluttertoast.showToast(msg: "onLaunch $message");
            },
          );
        });
      }

      Future<dynamic> myBackgroundMessageHandler(
          Map<String, dynamic> message) async {
        print("_backgroundMessageHandler");
        if (message.containsKey('data')) {
          // Handle data message
          final dynamic data = message['data'];
          print("_backgroundMessageHandler data: ${data}");
        }

        if (message.containsKey('notification')) {
          // Handle notification message
          final dynamic notification = message['notification'];
          print("_backgroundMessageHandler notification: ${notification}");
          Fimber.d("=====>myBackgroundMessageHandler $message");
        }
        return Future<void>.value();
      }

Answer:

      onBackgroundMessage: myBackgroundMessageHandler,
  onLaunch: (Map<String, dynamic> message) async {
    await print("onLaunch: $message");
    ///_navigateToItemDetail(message);
    await _showItemDialog(message); // Diyalog Kutusu Oluştur
  },
  onResume: (Map<String, dynamic> message) async {
    await print("onResume: $message");
    ///_navigateToItemDetail(message);      
    await _showItemDialog(message); // Diyalog Kutusu Oluştur
  },

Add await is working

Answer:

I move to a particular screen onResume and onLaunch on clicking notification.But I have still an issue when my app is terminated and I click on notification, my screen is open and after a few second My default app screen is open.pleasue guid.

Answer:

    onLaunch: (Map<String, dynamic> message) {
      print("I am here onLaunch");
      print(message);
      Navigator.push(
       context,
       MaterialPageRoute(
        builder: (context) => ScreenA()
       )
      );
    },
    onResume: (Map<String, dynamic> message) {
      print("I am here onResume");
      print(message);
      Navigator.push(
       context,
       MaterialPageRoute(
        builder: (context) => ScreenA()
       )
      );
    },

try this