Search code examples
flutterdartfirebase-cloud-messagingflutter-webfirebase-notifications

How to receive FCM push notification data in flutter web and redirect user to received link in notification?


I am trying to redirect user to specific page in flutter web on click of notification, till now I am receiving notification, the last open page is displayed in browser on click of notification, but I want user to redirect to specific page like specific post using unique post id, but I found no other ways, This thing happens in Android build, but doesn't happen on web build.

just take a look at ServiceWorker.js in serviceworker.js even console.log() in not executing on receiving notification.

messaging.setBackgroundMessageHandler(function (payload) {
    console.log(payload);
    const promiseChain = clients
        .matchAll({
            type: "window",
            includeUncontrolled: true
        })
        .then(windowClients => {
            for (let i = 0; i < windowClients.length; i++) {
                const windowClient = windowClients[i];
                windowClient.postMessage(payload);
            }
        })
        .then(() => {
            const title = payload.notification.title;
            const options = {
                body: payload.notification.body             
              };
            return registration.showNotification(title, options);
        });
    return promiseChain;
});
self.addEventListener('notificationclick', function (event) {
    console.log('notification received: ', event)
});

now the logic for sending FCM Push Notification:

  @required String message,
  @required String fcmToken,
  @required String title,
  String type,
  String typeId,
}) async {
  await http
      .post(
    Uri.parse('https://fcm.googleapis.com/fcm/send'),
    headers: <String, String>{
      'Content-Type': 'application/json',
      'Authorization': 'key=$serverToken',
    },
    body: jsonEncode(
      <String, dynamic>{
        'notification': <String, dynamic>{
          "click_action": 'FLUTTER_NOTIFICATION_CLICK',
          'body': message,
          'title': '$title',
          'sound': 'default'
        },
        'priority': 'high',
        'data': <String, dynamic>{
          'click_action': 'FLUTTER_NOTIFICATION_CLICK',
          'id': '1',
          'status': 'done',
          'type': type ?? "",
          'typeId': typeId ?? "",
        },
        'to': fcmToken.toString().trim(),
      },
    ),
  )
      .then((value) {
    print("Notification Sent");
  });
  final Completer<Map<String, dynamic>> completer =
      Completer<Map<String, dynamic>>();

  // firebaseMessaging.configure(
  //   onMessage: (Map<String, dynamic> message) async {
  //     completer.complete(message);
  //   },
  // );
  return completer.future;
}

Now the way I am handling Notification Receive:

    print("in setup interacted message");
    const AndroidNotificationChannel channel = AndroidNotificationChannel(
      'high_importance_channel', // id
      'High Importance Notifications', // title
      'This channel is used for important notifications.', // description
      importance: Importance.max,
    );

    final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
        FlutterLocalNotificationsPlugin();

    await flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
            AndroidFlutterLocalNotificationsPlugin>()
        ?.createNotificationChannel(channel);

    NotificationSettings notifSettings =
        await FirebaseMessaging.instance.requestPermission(
      alert: true,
      announcement: true,
      badge: true,
      carPlay: true,
      criticalAlert: true,
      provisional: true,
      sound: true,
    );

    if (notifSettings.authorizationStatus == AuthorizationStatus.authorized) {
      // Get any messages which caused the application to open from
      // a terminated state.
      try {
        RemoteMessage initialMessage =
            await FirebaseMessaging.instance.getInitialMessage();

        // If the message also contains a data property with a "type" of "post",
        // navigate to a post screen
        if (initialMessage != null && initialMessage.data['type'] == 'post') {
          await Navigator.push(
            context,
            new MaterialPageRoute(
              builder: (BuildContext context) => IndividualPostPage(
                postObjectId: initialMessage.data['typeId'],
              ),
            ),
          );
          print(initialMessage.data);
          initialMessage = null;
        }

        // Also handle any interaction when the app is in the background via a
        // Stream listener
        FirebaseMessaging.onMessageOpenedApp
            .listen((RemoteMessage message) async {
          print("here");
          if (message != null && message.data['type'] == 'post') {
            if (message.data['type'] == 'post') {
              await Navigator.push(
                context,
                new MaterialPageRoute(
                  builder: (BuildContext context) => IndividualPostPage(
                    postObjectId: message.data['typeId'],
                  ),
                ),
              );
            }
            print(message.data);
            message = null;
          }
        });
      } catch (e) {
        print("Error $e");
      }
    }
  }

Solution

  • messaging.setBackgroundMessageHandler(function (payload) {
        console.log(payload);
        const promiseChain = clients
            .matchAll({
                type: "window",
                includeUncontrolled: true
            })
            .then(windowClients => {
                for (let i = 0; i < windowClients.length; i++) {
                    const windowClient = windowClients[i];
                    windowClient.postMessage(payload);
                }
            })
            .then(() => {
                const title = payload.notification.title;
     var click_action =payload.data.ui_route;//ui route is ur route
    
                const options = {
                    body: payload.notification.body    ,
     data: {
                              click_action,
                              
                            }         
                  };
                return registration.showNotification(title, options);
            });
        return promiseChain;
    });
    // Notification click event listener
    self.addEventListener('notificationclick', e => {
    data=e.notification.data.obj;
      // Close the notification popout
      e.notification.close();
      // Get all the Window clients
      e.waitUntil(clients.matchAll({ type: 'window' }).then(clientsArr => {
        // If a Window tab matching the targeted URL already exists, focus that;
        const hadWindowToFocus = clientsArr.some(windowClient => windowClient.url === e.notification.data.click_action ? (windowClient.focus(), true) : false);
        // Otherwise, open a new tab to the applicable URL and focus it.
        if (!hadWindowToFocus) clients.openWindow(e.notification.data.click_action).then(windowClient => windowClient ? windowClient.focus() : null);
      }));
    });