10

I have firebase cloud messaging (FCM) and can send a message to my user.

However, I want to make a scenario that if my user taps notification message on their mobile phone, then the apps will open a view or pop up and do some background task.

is this possible?

Thank You

===Update Question as suggested by Shady Boshra

1) I create google cloud function's firebase using typeScript:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const fcm = admin.messaging();

export const sendNotif = functions.https.onRequest((request, response) => {

   const tokens = request.query.tokens;
   const payload: admin.messaging.MessagingPayload = {
   notification: {
      title: '[TEST-123] title...',
      body: `[TEST-123]  body of message... `,          
      click_action: 'FLUTTER_NOTIFICATION_CLICK',
      tag: "news",
      data: "{ picture: 'https://i.imgur.com/bY2bBGN.jpg', link: 'https://example.com' }"
    }
  };
  const res = fcm.sendToDevice(tokens, payload);
  response.send(res);
});

2) I update my Mobile Apps Code / Dart :

_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
  print("::==>onMessage: $message");

  message.forEach((k,v) => print("${k} - ${v}"));

  String link = "https://example.com";

  FlutterWebBrowser.openWebPage(url: link, androidToolbarColor: Pigment.fromString(UIData.primaryColor));
  return null;
},

then i try to send push notif when apps is in opened mode.

However, when i try to debug it, i cant find the content of data. it give return below response:

I/flutter (30602): ::==>onMessage: {notification: {body: [TEST-123] body of message... , title: [TEST-123] title...}, data: {}}

I/flutter (30602): ::==>onMessage: {notification: {body: [TEST-123] body of message... , title: [TEST-123] title...}, data: {}}

I/flutter (30602): notification - {body: [TEST-123] body of message... , title: [TEST-123] title...}

I/flutter (30602): data - {}

I/flutter (30602): notification - {body: [TEST-123] body of message... , title: [TEST-123] title...}

I/flutter (30602): data - {}

as you can see, the content of data is blank.

===Update 2

If I edit data by removing double quotes, it return error when running firebase deploy:

> functions@ build /Users/annixercode/myPrj/backend/firebase/functions
> tsc

src/index.ts:10:4 - error TS2322: Type '{ title: string; body: string; click_action: string; tag: string; data: { picture: string; link: string; }; }' is not assignable to type 'NotificationMessagePayload'.
  Property 'data' is incompatible with index signature.
    Type '{ picture: string; link: string; }' is not assignable to type 'string'.

10    notification: {
      ~~~~~~~~~~~~

  node_modules/firebase-admin/lib/index.d.ts:4246:5
    4246     notification?: admin.messaging.NotificationMessagePayload;
             ~~~~~~~~~~~~
    The expected type comes from property 'notification' which is declared here on type 'MessagingPayload'


Found 1 error.

npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! functions@ build: `tsc`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the functions@ build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/anunixercode/.npm/_logs/2019-08-28T01_02_37_628Z-debug.log

Error: functions predeploy error: Command terminated with non-zero exit code2

==Update 3 (response to Shady Boshra's answer)

below are my code on flutter:

  _firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) {
  print("::==>onMessage: $message");

  message.forEach((k,v) => print("${k} - ${v}"));

  var data = message['notification']['data']['link'];
  print ("===>data_notif = "+data.toString());

  var data2 = message['data']['link'];
  print ("===>data_notif2 = "+data2.toString());
...

after i send push notif, i just got below message in debug:

The application is paused.
Reloaded 22 of 1277 libraries.
I/flutter (18608): 0
I/flutter (18608): AsyncSnapshot<String>(ConnectionState.active, Tuesday, September 3, 2019, null)
I/flutter (18608): ::==>onMessage: {notification: {body: [TEST-123]  body of message... , title: [TEST-123] title...}, data: {}}
I/flutter (18608): notification - {body: [TEST-123]  body of message... , title: [TEST-123] title...}
I/flutter (18608): data - {}
Application finished.

as you can see, i cant get the value of link inside data

questionasker
  • 2,536
  • 12
  • 55
  • 119

3 Answers3

6

Sure, you can do it on Flutter too.

First, I expect you set these codes in your manifest

<intent-filter> <action android:name="FLUTTER_NOTIFICATION_CLICK" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter>

In your notification body in JSON in the backend (node.js) add tag element.

const payload: admin.messaging.MessagingPayload = {
  notification: {
    title: 'New Puppy!',
    body: `${puppy.name} is ready for adoption`,
    icon: 'your-icon-url',
    tag: 'puppy', 
    data: {"click_action": "FLUTTER_NOTIFICATION_CLICK", "id": "1", "status": "done"}, 
    click_action: 'FLUTTER_NOTIFICATION_CLICK' // required only for onResume or onLaunch callbacks
  }
};

And in Dart code, make sure to set this code in the first initState() page.

_fcm.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        var tag = message['notification']['title']);

        if (tag == 'puppy') 
        {
         // go to puppy page
        } else if (tag == 'catty') 
        {
         // go to catty page
        } 
    },
    onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        // TODO optional
    },
    onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        // TODO optional
    },
  );

The most of my answer is from this blog.

It mentioned in the blog when the callbacks fires, so decide closely which one set your code in.

onMessage fires when the app is open and running in the foreground.

onResume fires if the app is closed, but still running in the background.

onLaunch fires if the app is fully terminated.

Update

as your question has been updated, please consider the following from this article

• Notification Messages - Consist of a title and a message body and trigger the notification system on arrival at the device. In other words, an icon will appear in the status bar and an entry will appear in the notification shade. Such notifications should be used when sending an informational message that you want the user to see.

ex:

var payload = {
  notification: {
    title: "Account Deposit",
    body: "A deposit to your savings account has just cleared."
  }
};

• Data Messages - Contain data in the form of key/value pairs and are delivered directly to the app without triggering the notification system. Data messages are used when sending data silently to the app.

ex:

var payload = {
  data: {
    account: "Savings",
    balance: "$3020.25"
  }
};

• Combined Messages – Contain a payload comprising both notification and data. The notification is shown to the user and the data is delivered to the app.

ex:

var payload = {
  notification: {
    title: "Account Deposit",
    body: "A deposit to your savings account has just cleared."
  },
  data: {
    account: "Savings",
    balance: "$3020.25"
  }
};

So, I think you will use the third one, which sending notification and data in the same time.

You can simply access the data using dart as below.

message['data']['account']

Update 2

As you updated your question and my solution didn't work with you in a good manner. I decided to test it myself, and guess what ! It works fine with your and same codes.

My node.js backend code

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();
const fcm = admin.messaging();

exports.sendNotification = functions.firestore
    .document('orders/{orderId}')
    .onCreate(async snapshot => {

        const order = snapshot.data();

        // Token of my device only
        const tokens = ["f5zebdRcsgg:APA91bGolg9-FiLlCd7I0LntNo1_7b3CS5EAJBINZqIpaz0LVZtLeGCvoYvfjQDhW0Qdt99jHHS5r5mXL5Up0kBt2M7rDmXQEqVl_gIpSQphbaL2NhULVv3ZkPXAY-oxX5ooJZ40TQ2-"];

        const payload = {
            notification: {
                title: "Account Deposit",
                body: "A deposit to your savings account has just cleared."
            },
            data: {
                account: "Savings",
                balance: "$3020.25",
                link: "https://somelink.com"
            }
        };

        return fcm.sendToDevice(tokens, payload);
    });

and dart code

final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();

  @override
  void initState() {
    super.initState();

    _firebaseMessaging.requestNotificationPermissions();

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");

        var data2 = message['data']['link'];
        print ("===>data_notif2 = "+data2.toString());

        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            content: ListTile(
              title: Text(message['notification']['title']),
              subtitle: Text(message['notification']['body']),
            ),
            actions: <Widget>[
              FlatButton(
                child: Text('Ok'),
                onPressed: () => Navigator.of(context).pop(),
              ),
            ],
          ),
        );
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        // TODO optional
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        // TODO optional
      },
    );

    _saveDeviceToken();
  }

  /// Get the token, save it to the database for current user
  _saveDeviceToken() async {
    // Get the current user
    String uid = 'jeffd23';
    // FirebaseUser user = await _auth.currentUser();

    // Get the token for this device
    String fcmToken = await _firebaseMessaging.getToken();

    // Save it to Firestore
    if (fcmToken != null) {
      print(fcmToken);
    }
  }

and the console output

I/flutter (20959): onMessage: {notification: {title: Account Deposit, body: A deposit to your savings account has just cleared.}, data: {account: Savings, balance: $3020.25, link: https://somelink.com}}
I/flutter (20959): ===>data_notif2 = https://somelink.com

So I believe the problem is somehow in your codes, not in the solution I give to you. Please clearly look for the problem you may have. Hope you find it and work well for you.

Shady Boshra
  • 2,521
  • 3
  • 23
  • 35
  • i have tried your solution, but still not using `nodejs` but firebase console. i receive message on state of `onMessage`, but for `onLaunch` and `onResume` i cant get it on debug console. any idea ? – questionasker Aug 26 '19 at 09:54
  • I updated my answer, please read it all again, But you won't be able to continue using the Firebase console only, you should then move on and use backend, node.js as an example. – Shady Boshra Aug 26 '19 at 10:25
  • i have existing `FLUTTER_NOTIFICATION_CLICK` intent on my Android Manifest. but it does not work... – questionasker Aug 26 '19 at 11:41
  • And did you add `data` attribute inside `notification` in `payload` ??, I think you should try in backend instead of Firebase console now. – Shady Boshra Aug 26 '19 at 11:43
  • Or if you insist to do it from console, please read this quote from [firebase_messaging](https://pub.dev/packages/firebase_messaging) package. **For testing purposes, the simplest way to send a notification is via the Firebase Console. Make sure to include click_action: FLUTTER_NOTIFICATION_CLICK as a "Custom data" key-value-pair (under "Advanced options") when targeting an Android device. The Firebase Console does not support sending data messages.** – Shady Boshra Aug 26 '19 at 11:45
  • I hope if my answer solved your question, please make it as correct answer. Thanks – Shady Boshra Aug 27 '19 at 15:08
  • I did also update my answer, and I do believe the problem is your side not in the solution I give. Hope you find your way. – Shady Boshra Sep 03 '19 at 10:42
  • okay, now it solved... but your code examples return `client access denied` on http request – questionasker Sep 05 '19 at 10:21
  • This error is related to your backend firebase security, you can read about [it here](https://stackoverflow.com/questions/37403747/firebase-permission-denied). Glad to hear that your question is solved. – Shady Boshra Sep 05 '19 at 10:24
  • 1
    Now it can solved, because my previous code (firebase) was using `typeScript` instead of `javascript` – questionasker Sep 05 '19 at 11:34
  • 1
    Oh I see. Wish you perfect time coding. – Shady Boshra Sep 05 '19 at 11:35
2

Yes it is possible in native but I'm not sure in flutter. You can build a PendingIntent with a back stack.

Intent notifIntent = new Intent(this, notifActivity.class);
TaskStackBuilder stkBuilder = TaskStackBuilder.create(this);
stkBuilder.addNextIntentWithParentStack(notifIntent);
PendingIntent resultPendingIntent =
        stkBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

Then you should pass the PendingIntent to the notification.

check this link: https://developer.android.com/training/notify-user/navigation

Saeiddjawadi
  • 312
  • 2
  • 13
0

The Easiest method is using OneSignal if you don't have enough time to build the backend to send push notif using firebase, however, OneSignal has a limit on his subscriber (free until 30,000 subscribers).

questionasker
  • 2,536
  • 12
  • 55
  • 119