flutter_local_notifications icon indicating copy to clipboard operation
flutter_local_notifications copied to clipboard

[iOS] onDidReceiveNotificationResponse doesn't trigger when clicking on DarwinNotificationAction

Open mvreenv opened this issue 6 months ago • 4 comments

Describe the bug I'm working on a mobile app on Android and iOS, with firebase_messaging. The problem I'm having depending on the platform I have two completely different behaviors when clicking an action on a notification.

  • on Android it works as expected: when I click on an AndroidNotificationAction, onDidReceiveNotificationResponse triggers and I can extract the actionId from the NotificationResponse object.
  • on iOS: when I click on a DarwinNotificationAction depending on the app state, either FirebaseMessaging.onMessage.listen or FirebaseMessaging.onBackgroundMessage trigger, with the same RemoteMessage I received originally, and no NotificationResponse object (obviously). I noticed that onDidReceiveNotificationResponse is called on iOS when swiping away a notification, which is not intuitive at all, and still doesn't contain actionId (since no DarwinNotificationAction was clicked)

To Reproduce I'm sending a notification from a C# BE Service using the FirebaseAdmin package, with this payload

var message = new Message
{
	Token = registrazioneId,
	Data = new Dictionary<string, string>
	{
		{ "messaggio", messageData.Message },
		{ "type", messageData.Category.ToString() },
		{ "uniqueid", messageData.UniqueId.ToString() },
		{ "subjectId", messageData.SubjectId?.ToString() ?? string.Empty }
	},
	Android = new AndroidConfig
	{
		Priority = Priority.High,
		DirectBootOk = true, 
	},
	Apns = new ApnsConfig
	{
		Aps = new Aps
		{
			Sound = "pushvolumealto.mp3",
			Alert = new ApsAlert
			{
				Body = messageData.Message,
			},
			Badge = 0,
			Category = category,
                }
	}
};

Expected behavior I would expect the iOS implementation to work the same way as the Android one, and being able to extract which action button has been clicked on iOS notifications.

Sample code to reproduce the problem Dependencies:

  firebase_core: ^3.13.0
  firebase_messaging: ^15.2.5
  flutter_local_notifications: ^19.2.1

My FirebaseMessagingService class (I omitted parts of the code that are not relevant to the bug)

import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';

import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:geolocator/geolocator.dart';
import 'package:resultify/resultify.dart';

@pragma('vm:entry-point')
enum NotificationActions {
  gestisci,
  accetta;

  @pragma('vm:entry-point')
  getId() {
    switch (this) {
      case NotificationActions.gestisci:
        return 'gestisci_action';
      case NotificationActions.accetta:
        return 'accetta_action';
    }
  }

  @pragma('vm:entry-point')
  getLabel() {
    switch (this) {
      case NotificationActions.gestisci:
        return 'Gestisci';
      case NotificationActions.accetta:
        return 'Accetta Chiamata';
    }
  }
}

@pragma('vm:entry-point')
class FirebaseMessagingService {
  static Future<void> initialize() async {
    log('[Firebase Messaging Service] Initializining...');

    await FirebaseCrashlyticsService.initialize();

    InitializationSettings initializationSettings = InitializationSettings(
      android:
          const AndroidInitializationSettings('@mipmap/ic_launcher_foreground'),
      iOS: DarwinInitializationSettings(
        requestSoundPermission: true,
        requestAlertPermission: true,
        requestBadgePermission: true,
        notificationCategories: [
          DarwinNotificationCategory(
            _iosNuovaChiamataCategoryId,
            actions: [
              DarwinNotificationAction.plain(
                NotificationActions.gestisci.getId(),
                NotificationActions.gestisci.getLabel(),
                options: {
                  DarwinNotificationActionOption.foreground,
                },
              ),
              DarwinNotificationAction.plain(
                NotificationActions.accetta.getId(),
                NotificationActions.accetta.getLabel(),
                options: {
                  DarwinNotificationActionOption.foreground,
                },
              ),
            ],
          ),
          DarwinNotificationCategory(
            _iosChiamataReminderCategoryId,
            actions: [
              DarwinNotificationAction.plain(
                NotificationActions.gestisci.getId(),
                NotificationActions.gestisci.getLabel(),
                options: {
                  DarwinNotificationActionOption.foreground,
                },
              ),
            ],
          )
        ],
      ),
    );

    await _flutterNotificationsPlugin.initialize(
      initializationSettings,
      onDidReceiveNotificationResponse: _onNotificationActionTap,
      onDidReceiveBackgroundNotificationResponse: _onNotificationActionTap,
    );

    await FirebaseMessaging.instance
        .setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );

    // app open
    FirebaseMessaging.onMessage.listen(_onMessage);

    // app in the background
    FirebaseMessaging.onBackgroundMessage(_onBgMessage);

    // app is in the background and the user tapped a notification
    FirebaseMessaging.onMessageOpenedApp.listen(_onBgMessage);

    // app was terminated
    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage? message) {
      if (message != null) {
        _onMessageFromKilledState(message);
      }
    });

    log('[Firebase Messaging Service] Initialization complete.');
  }

// ...

@pragma('vm:entry-point')
  static Future<void> _onNotificationActionTap(
    NotificationResponse? notificationResponse,
  ) async {
    log('[Firebase Messaging Service] Notification tapped');

    if (notificationResponse?.actionId ==
           NotificationActions.gestisci.getId() &&
        NavigationService().currentRoute() != Routes.chiamateList) {
      NavigationService().pushNamed(Routes.chiamateList);
    }

    if (notificationResponse?.actionId ==
            NotificationActions.accetta.getId() &&
        notificationResponse?.payload != null) {
      await _executeAccettaChiamata(
        chiamataId: notificationResponse!.payload!, // ! PAYLOAD
      );
    }
  }

}

mvreenv avatar Jun 13 '25 12:06 mvreenv

Ran into same issue, App Delegate is configured as guided to make any communication available in the action isolate. Works fine on Android, but unexpectedly same code which was working fine stopped working and now onDidReceiveNotificationResponse and onDidReceiveBackgroundNotificationResponse don't calls on iOS. I have the same set up as you @mvreenv. Did you find any fix? if so kindly share it, otherwise will have to wait for response from authors.

AdnanKhan45 avatar Jun 30 '25 09:06 AdnanKhan45

@mvreenv are you able to create a repository hosting a minimal app that can reproduce the issue? I believe this sounds like a case for the team managing the Flutter FCM plugin to address. A long time ago when Google were the ones managing it, I submitted a PR that was merged to avoid a problem where it was processing the notifications from other plugins like this one. The fix involved making sure the FCM plugin was only processing notifications from FCM. What you've described sounds like there are some scenarios within the FCM plugin to avoid conflicting with other plugins. Best way for the team managing FCM to look into this is to raise an issue on their repo, give a link the repo and give the steps to reproduce the problem through the repo

MaikuB avatar Jul 05 '25 06:07 MaikuB

@MaikuB I'm waiting to see if an issue on the FCM repo gets resolved, there are currently problems with callbacks not triggering on iOS so I think it could all be related. Once there is a fix over there I'll test and let you know if this issue with onDidReceiveNotificationResponse is still present or it's resolved.

mvreenv avatar Jul 07 '25 09:07 mvreenv

You're missing a key part that's in the example but not clearly documented. Add this during initialization to handle app launch via notification:

  final NotificationAppLaunchDetails? notificationAppLaunchDetails = !kIsWeb &&
          Platform.isLinux
      ? null
      : await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
  String initialRoute = HomePage.routeName;
  if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
    selectedNotificationPayload =
        notificationAppLaunchDetails!.notificationResponse?.payload;
    initialRoute = SecondPage.routeName;
  }

This checks if the app was launched by tapping a notification and updates the initial route accordingly.

rajsatish4ar avatar Jul 11 '25 05:07 rajsatish4ar