2

I'm creating a chat application and i want to use fcm to send notification if the person has a new message, but i don't know how to proceed. All the tutorials i found use to send the message from firebase. But i want to send it automatically when there is a new message for the person

Pondikpa Tchabao
  • 1,853
  • 5
  • 13
  • 20
  • 2
    https://stackoverflow.com/questions/37371990/how-can-i-send-a-firebase-cloud-messaging-notification-without-use-the-firebase – Shyju M Jan 26 '19 at 18:45
  • There's a [Dart package](https://pub.dartlang.org/packages/fcm_push) to send the notifications too. – Richard Heap Jan 26 '19 at 21:08
  • How to send this with flutter? DATA='{"notification": {"body": "this is a body","title": "this is a title"}, "priority": "high", "data": {"click_action": "FLUTTER_NOTIFICATION_CLICK", "id": "1", "status": "done"}, "to": ""}' curl https://fcm.googleapis.com/fcm/send -H "Content-Type:application/json" -X POST -d "$DATA" -H "Authorization: key=" – Pondikpa Tchabao Feb 24 '19 at 16:56
  • You can use normal rest API for FCM notification. Check this out for more details - https://arkapp.medium.com/flutter-fcm-rest-api-7719925f2e3e – abdul rehman Apr 01 '21 at 06:03

3 Answers3

10

A possible workaround if you use firebase should be like this:

You need to store each firebase FCM token for a specific user (need to take in account here that a user can log in at the same time on his account from multiple devices) so you can store the userId and his deviceUniqueId on flutter you can get it from device_info https://pub.dev/packages/device_info:

  String identifier;
  final DeviceInfoPlugin deviceInfoPlugin = new DeviceInfoPlugin();
  try {
    if (Platform.isAndroid) {
      var build = await deviceInfoPlugin.androidInfo;
      identifier = build.id.toString();
    } else if (Platform.isIOS) {
      var data = await deviceInfoPlugin.iosInfo;
      identifier = data.identifierForVendor;//UUID for iOS
    }
  } on PlatformException {
    print('Failed to get platform version');
  }

and after that to get the unique CFM token that Firebase provide for each device, you can get it using Firebase firebase_messaging plugin (https://pub.dev/packages/firebase_messaging) getToken() and insert the token to firestore (or an other database you want to store it)

  FirebaseMessaging firebaseMessaging = new FirebaseMessaging();

  firebaseMessaging.requestNotificationPermissions(
      const IosNotificationSettings(sound: true, badge: true, alert: true));
  firebaseMessaging.onIosSettingsRegistered
      .listen((IosNotificationSettings settings) {
    print("Settings registered: $settings");
  });

  firebaseMessaging.getToken().then((token){

    print('--- Firebase toke here ---');
    Firestore.instance.collection(constant.userID).document(identifier).setData({ 'token': token});
    print(token);

  });

After that you can insert one or more FCM token connected to multiple device for one user. 1 user ... n devices , 1 device ... 1 unique token to get push notifications from Firebase.

send it automatically when there is a new message for the person : now you need to call the Firestore API(is very fast indeed but need to be careful about the plan limit that you are using here) or another API call if you store the token to another db, in order to get the token/tokens for each user and send the push notifications.

To send the push notification from flutter you can use a Future async function. P.s: I'm passing as argument a List here in order to use "registration_ids" instead of "to" and send the push notification to multiple tokens if the user has been logged in on multiple devices.

Future<bool> callOnFcmApiSendPushNotifications(List <String> userToken) async {

  final postUrl = 'https://fcm.googleapis.com/fcm/send';
  final data = {
    "registration_ids" : userToken,
    "collapse_key" : "type_a",
    "notification" : {
      "title": 'NewTextTitle',
      "body" : 'NewTextBody',
    }
  };

  final headers = {
    'content-type': 'application/json',
    'Authorization': constant.firebaseTokenAPIFCM // 'key=YOUR_SERVER_KEY'
  };

  final response = await http.post(postUrl,
      body: json.encode(data),
      encoding: Encoding.getByName('utf-8'),
      headers: headers);

  if (response.statusCode == 200) {
    // on success do sth
    print('test ok push CFM');
    return true;
  } else {
    print(' CFM error');
    // on failure do sth
    return false;
  }
}

You can also check the post call from postman in order to make some tests. POST request On Headers add the:

  1. key Authorization with value key=AAAAO........ // Project Overview -> Cloud Messaging -> Server Key
  2. key Content-Type with value application/json

And on the body add

{
 "registration_ids" :[ "userUniqueToken1", "userUniqueToken2",... ],
 "collapse_key" : "type_a",
 "notification" : {
     "body" : "Test post",
     "title": "Push notifications E"
 }
}

"registration_ids" to send it to multiple tokens (same user logged in to more than on device at the same time) "to" in order to send it to a single token (one device per user / or update always the user token that is connected with his device and have 1 token ... 1 user)

I'm making an edit to the response, in order to add that is very important to add the FCM Server Key on a trusted environment or server!

SwiftiSwift
  • 4,046
  • 5
  • 27
  • 57
nike
  • 485
  • 5
  • 12
  • 3
    *firebaser here* Using `"key=your_server_key"` in client-side code is a serious security risk, as it allows malicious users to send whatever message they want to your users. This is a bad practice and should not be used in production-level applications. For a better approach, see https://fireship.io/lessons/flutter-push-notifications-fcm-guide/ and my answer here: https://stackoverflow.com/a/57842615 – Frank van Puffelen Dec 16 '19 at 04:52
  • Yes indeed, this one is for development purpose as the postman example above too, I'm editing the answer, because also me have set the server key on server, when the API is invoked! – nike Dec 16 '19 at 13:45
  • how we can do that 'need to take in account here that a user can log in at the same time on his account from multiple devices' – Merym Sep 10 '20 at 16:36
  • I'm taking as an example the use of Cloud Firestore when you can save the metadata that you need to use but indeed you can use another DB sure. You can save on the **Collection** the user id, on each **Document** the device ID that is unique from each device on Flutter using device_info https://pub.dev/packages/device_info you can get that information very straightforward and on the **Collection** you can store the Firebase Token in order to send the push notification on that device. In that way 1 use can have multiple devices and each device will have one token. Sorry for the late res – nike Dec 17 '20 at 09:21
  • i currently have 2 android apps in my firebase project, how can i specify which app the notification is related to? – Fethi Jan 25 '21 at 11:21
4

I'll list here a few related questions which I have participated with answers. I guess you'll find a lot of relevant info on using firebase cloud messaging (FCM) in a chat app.

  1. Is FCM the only way to build a chat app ?
  2. Suggested approach to use FCM in a chat app
  3. Is using topics a better solution then using the fcmToken in a chat app?
  4. Problems with FCM onMessage while app is in background
  5. Problem: after logoff, user continues to receive notifications

Good luck!

Feu
  • 4,133
  • 1
  • 23
  • 48
2
    //Notification Sending Side Using Dio flutter Library to make http post request

        static Future<void> sendNotification(receiver,msg)async{

        var token = await getToken(receiver);
        print('token : $token');

        final data = {
          "notification": {"body": "Accept Ride Request", "title": "This is Ride Request"},
          "priority": "high",
          "data": {
            "click_action": "FLUTTER_NOTIFICATION_CLICK",
            "id": "1",
            "status": "done"
          },
          "to": "$token"
        };

        final headers = {
          'content-type': 'application/json',
          'Authorization': 'key=AAAAY2mZqb4:APA91bH38d3b4mgc4YpVJ0eBgDez1jxEzCNTq1Re6sJQNZ2OJvyvlZJYx7ZASIrAj1DnSfVJL-29qsyPX6u8MyakmzlY-MRZeXOodkIdYoWgwvPVhNhJmfrTC6ZC2wG7lcmgXElA6E09'
        };


        BaseOptions options = new BaseOptions(
          connectTimeout: 5000,
          receiveTimeout: 3000,
          headers: headers,
        );


        try {
          final response = await Dio(options).post(postUrl,
              data: data);

          if (response.statusCode == 200) {
            Fluttertoast.showToast(msg: 'Request Sent To Driver');
          } else {
            print('notification sending failed');
            // on failure do sth
          }
        }
        catch(e){
          print('exception $e');
        }




      }

      static Future<String> getToken(userId)async{

        final Firestore _db = Firestore.instance;

        var token;
        await _db.collection('users')
            .document(userId)
            .collection('tokens').getDocuments().then((snapshot){
              snapshot.documents.forEach((doc){
                token = doc.documentID;
              });
        });

        return token;


      }


    //Now Receiving End 

        class _LoginPageState extends State<LoginPage>
        with SingleTickerProviderStateMixin {

      final Firestore _db = Firestore.instance;
      final FirebaseMessaging _fcm = FirebaseMessaging();

      StreamSubscription iosSubscription;



    //this code will go inside intiState function 

    if (Platform.isIOS) {
          iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
            // save the token  OR subscribe to a topic here
          });

          _fcm.requestNotificationPermissions(IosNotificationSettings());
        }
        _fcm.configure(
          onMessage: (Map<String, dynamic> message) async {
            print("onMessage: $message");
            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
          },
        );

//saving token while signing in or signing up
 _saveDeviceToken(uid) async {
    // FirebaseUser user = await _auth.currentUser();

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

    // Save it to Firestore
    if (fcmToken != null) {
      var tokens = _db
          .collection('users')
          .document(uid)
          .collection('tokens')
          .document(fcmToken);

      await tokens.setData({
        'token': fcmToken,
        'createdAt': FieldValue.serverTimestamp(), // optional
        'platform': Platform.operatingSystem // optional
      });
    }
  }
Zeeshan Mehdi
  • 617
  • 9
  • 14
  • Your answer background i understand but codes are not clear. What all the firebase components have you used. Better if you can add included files in this page and also want to know why you are using DIO plugin. – Rocx Dec 03 '20 at 16:30