Mohammad Abd-Elmoniem
Mohammad Abd-Elmoniem

Reputation: 666

Send notification to specific user firebase in flutter

How do I send a notification to another user when one user presses a button? Can someone show me a code snippet?

I realize that this question was asked before, however, it was closed since there were "several answers." The links that were provided that were similar did not explain sending notifications in flutter.

Upvotes: 9

Views: 15810

Answers (2)

Mohammad Abd-Elmoniem
Mohammad Abd-Elmoniem

Reputation: 666

I have figured out how send a notification to another device using an in app feature.

First, you will need to import the necessary packages:

firebase_messaging
flutter_local_notifications

Note: you will also use the http package

Also note: to send notifications to another device, you must know the device token of that device. I prefer getting the token and saving it in Firestore or Realtime Database. Here is the code to get the device token.

String? mtoken = " ";

void getToken() async {
    await FirebaseMessaging.instance.getToken().then((token) {
      setState(() {
        mtoken = token;
      });
    });
  }

The token will be saved in mtoken, you can now use this as the token for the coming steps.

Code for sending notifications

Enable FIrebase Cloud Functions in your Firebase project, this must use the Firbebase Blaze Plan.

In my functions folder, I added this code in index.js

 /* eslint-disable */
const functions = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();

exports.sendNotification = functions.https.onCall(async (data, context) => {
  await admin.messaging().sendMulticast({
  tokens: data.tokens,
  notification: {
    title: data.title,
    body: data.body,
    imageUrl: data.imageUrl,
  },
});

Make sure to deploy your Firebase Cloud Function. You'll know if it works, if you see this.

enter image description here

You can call this function in your Flutter app with this code

Future<void> sendNotification(
  tokens,
  String title,
  String body,
  String imageUrl,
) async {
  FirebaseFunctions functions =
      FirebaseFunctions.instanceFor(region: 'us-central1');

  try {
    final HttpsCallable callable = functions.httpsCallable('sendNotification');
    final response = await callable.call({
      'tokens': tokens,
      'title': title,
      'body': body,
      'imageUrl': imageUrl,
    });

    print('Message sent: ${response.data}');
  } catch (e) {
    print('Error sending message: $e');
  }
}

Code for receiving notifications

The next step is to request permission to send push notifications to your app.

  void requestPermission() async {
    FirebaseMessaging messaging = FirebaseMessaging.instance;

    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      print('User granted permission');
    } else if (settings.authorizationStatus ==
        AuthorizationStatus.provisional) {
      print('User granted provisional permission');
    } else {
      print('User declined or has not accepted permission');
    }
  }

(If you get "User declined or has not accepted permission" in your console, try going out of your app, finding the icon in the homescreen, pressing and holding on the app icon, tapping "App Info", tapping "Notifications" and turn on "All [app name] notifications."

You will also need two functions to load a Firebase Cloud Messaging notification and one to listen for a notification.

Code to load a Firebase Cloud Messaging notification:

 void loadFCM() async {
    if (!kIsWeb) {
      channel = const AndroidNotificationChannel(
        'high_importance_channel', // id
        'High Importance Notifications', // title
        importance: Importance.high,
        enableVibration: true,
      );

      flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

      /// Create an Android Notification Channel.
      ///
      /// We use this channel in the `AndroidManifest.xml` file to override the
      /// default FCM channel to enable heads up notifications.
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(channel);

      /// Update the iOS foreground notification presentation options to allow
      /// heads up notifications.
      await FirebaseMessaging.instance
          .setForegroundNotificationPresentationOptions(
        alert: true,
        badge: true,
        sound: true,
      );
    }
  } 

And this function to listen for a Firebase Cloud Messaging notifcation.

void listenFCM() async {
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      RemoteNotification? notification = message.notification;
      AndroidNotification? android = message.notification?.android;
      if (notification != null && android != null && !kIsWeb) {
        flutterLocalNotificationsPlugin.show(
          notification.hashCode,
          notification.title,
          notification.body,
          NotificationDetails(
            android: AndroidNotificationDetails(
              channel.id,
              channel.name,
              // TODO add a proper drawable resource to android, for now using
              //      one that already exists in example app.
              icon: 'launch_background',
            ),
          ),
        );
      }
    });
  }

You will want to run loadFCM, listenFCM, and requestPermission when the page is initialized.

  void initState() {
    super.initState();
    requestPermission();
    loadFCM();
    listenFCM();
  }

Outdated method

This code is deprecated and contains security issues due to the use of an API key with the app, which can be reverse engineered to send notifications as your app wih your API key. Use this only for testing since you do not need Firebase Cloud Functions for this.

The next step is to find your Firebase Cloud Messaging API key. This can simply be done by heading to your Firebase project > Project Settings > Cloud Messaging then copy the API key under Cloud Messaging API (Legacy).

When you have your Firebase Cloud Messaging API key, this is the code to display a notification given the notification title, body, and device token to send it to.

  void sendPushMessage(String body, String title, String token) async {
    try {
      await http.post(
        Uri.parse('https://fcm.googleapis.com/fcm/send'),
        headers: <String, String>{
          'Content-Type': 'application/json',
          'Authorization':
              'key=REPLACETHISWITHYOURAPIKEY',
        },
        body: jsonEncode(
          <String, dynamic>{
            'notification': <String, dynamic>{
              'body': body,
              'title': title,
            },
            'priority': 'high',
            'data': <String, dynamic>{
              'click_action': 'FLUTTER_NOTIFICATION_CLICK',
              'id': '1',
              'status': 'done'
            },
            "to": token,
          },
        ),
      );
      print('done');
    } catch (e) {
      print("error push notification");
    }
  }

Now you can call this function like this:

sendPushMessage('Notification Body', 'Notification Title', 'REPLACEWITHDEVICETOKEN');

I hope this helps.

Upvotes: 18

Roman Jaquez
Roman Jaquez

Reputation: 2779

You will need Firebase Cloud Messaging for that.

The way I've done it is using a Cloud Function that you can trigger via HTTP or even via a Firestore trigger, like this:


// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

/**
 * Triggered by a change to a Firestore document.
 *
 * @param {!Object} event Event payload.
 * @param {!Object} context Metadata for the event.
 */
exports.messageNotificationTrigger = (change, context) => {
 
  db.collection('users').get().then((snapshot) => {
    snapshot.docs.forEach(doc => {
      
      const userData = doc.data();
      
      if (userData.id == '<YOUR_USER_ID>') {
         admin.messaging().sendToDevice(userData.deviceToken, {
           notification: { 
             title: 'Notification title', body: 'Notification Body'}
           });
      }
    });
  });
};

Every user you have registered in your users collection must have a device token, sent from their device they access the app.

From Flutter, using the FCM package, this is how you send the device token to Firebase:

// fetch the device token from the Firebase Messaging instance
      // and store it securely on Firebase associated with this user uid
      FirebaseMessaging.instance.getToken().then((token) {
        FirebaseFirestore.instance.collection('users').doc(userCreds.user!.uid).set({
          'deviceToken': token
        });
      });

Where userCredentials.user!.uid is the user you use to log in to your application using Firebase Authentication like this:

UserCredential userCreds = await FirebaseAuth.instance.signInWithCredential(credential);

Hope that helps.

Upvotes: 3

Related Questions