MaikuB / flutter_local_notifications

A Flutter plugin for displaying local notifications on Android, iOS, macOS and Linux
2.47k stars 1.4k forks source link

IOS can't tap notification #2404

Closed Erchil66 closed 1 month ago

Erchil66 commented 2 months ago

Describe the bug When app is open or foreground, Then tapping notification doesn't do anything in IOS.

To Reproduce

  1. Simply try to received a message
  2. Click Notification in IOS(Heads-up)
  3. After clicking it doesn't do anything, It just cleared the notification.
  4. Don't have error occurred.

Expected behavior Should redirect to page,Platform is IOS

Sample code to reproduce the problem I created a singleton file for notification with firebase as my current code: It's working well on Android, It do redirect when tapping.

@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
  log("${notificationResponse.payload}", name: "NOTIF DETAILSSS");
  if (notificationResponse.payload != null) {
    final decodedData = jsonDecode(notificationResponse.payload!);
    final root = Rooted.fromJson(decodedData);
    final title = root.subject!.title ?? "";
    final id = root.payload?.threadId ?? "";
    final type = root.payload?.threadType;
    LocalPushPage.whichPage(
        id: id,
        type: "$type",
        isRemovedOrArchive:
            title.contains("Removed") || title.contains("has been archived"));
  }
}

late FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin;
const AndroidNotificationChannel channel = AndroidNotificationChannel(
  'high_importance_channel',
  'High Importance Notifications',
  description: 'This channel is used for important notifications.',
  importance: Importance.max,
);

final dawrinSetting = DarwinInitializationSettings(
  requestAlertPermission: true,
  requestBadgePermission: true,
  requestSoundPermission: true,
  onDidReceiveLocalNotification: (id, title, body, payload) {
    // ignore: avoid_print
    print(
      "$payload NOW",
    );
  },
);

class NotificationManager {
  static final NotificationManager _instance = NotificationManager._internal();

  final FirebaseMessaging _firebaseMessaging;

  NotificationManager._internal()
      : _firebaseMessaging = FirebaseMessaging.instance;

  factory NotificationManager() {
    return _instance;
  }

  Future<void> initNotifications() async {
    _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
    final initializationSettings = InitializationSettings(
      android: const AndroidInitializationSettings('drawable/splash_icon'),
      iOS: dawrinSetting,
    );
    await _flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
      onDidReceiveNotificationResponse: notificationTapBackground,
    );
    final NotificationAppLaunchDetails? notificationAppLaunchDetails =
        await _flutterLocalNotificationsPlugin
            .getNotificationAppLaunchDetails();
    final didNotificationLaunchApp =
        notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
    if (didNotificationLaunchApp == true) {
      final dataX = notificationAppLaunchDetails?.notificationResponse!.payload;

      await SharedPrefs.write(StringNames.forTerminateClickap, "$dataX");
    }

    if (Platform.isAndroid &&
        !(await _flutterLocalNotificationsPlugin
                .resolvePlatformSpecificImplementation<
                    AndroidFlutterLocalNotificationsPlugin>()
                ?.areNotificationsEnabled() ??
            false)) {
      await _flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(channel);
    }

    if (Platform.isIOS) {
      await _firebaseMessaging.setForegroundNotificationPresentationOptions(
        alert: true,
        badge: true,
        sound: true,
      );
      await _flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>()
          ?.requestPermissions(
            alert: true,
            badge: true,
            sound: true,
            critical: true,
          );
    }
  }

  void listenToFirebaseMessages() {
    FirebaseMessaging.instance.getInitialMessage().then((message) {
      if (message != null) {
        final id = "${message.data["data"]["thread_id"]}";
        final type = "${message.data["data"]["thread_type"]}";
        final title = message.notification!.title ?? "";

        log("'A new onMessageOpenedApp event was published! LOG NOTIF ON OPEN APP $id $type $title");
        LocalPushPage.whichPage(
          id: id,
          type: type,
          isRemovedOrArchive:
              title.contains("Removed") || title.contains("has been archived"),
        );
      }
    });
    FirebaseMessaging.onMessage.listen(showNotification);

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      log("${message.data}", name: "ON OPENED NOTIF");
      final id = "${message.data["data"]["thread_id"]}";
      final type = "${message.data["data"]["thread_type"]}";
      final title = message.notification!.title ?? "";
      log("'A new onMessageOpenedApp event was published! LOG NOTIF ON OPEN APP $id $type $title");
      LocalPushPage.whichPage(
          id: id,
          type: type,
          isRemovedOrArchive:
              title.contains("Removed") || title.contains("has been archived"));
    });
  }

  void showNotification(RemoteMessage message) {
    RemoteNotification? notification = message.notification;
    if (message.data.isNotEmpty) {
      log("${message.data}");
      _flutterLocalNotificationsPlugin.show(
        notification.hashCode,
        message.data["title"] ?? notification?.title ?? "",
        message.data["body"] ?? notification?.body ?? "",
        const NotificationDetails(
          iOS: DarwinNotificationDetails(
            threadIdentifier: 'high_importance_channel',
            presentAlert: true,
            presentBadge: true,
            presentSound: true,
            attachments: <DarwinNotificationAttachment>[],
            interruptionLevel: InterruptionLevel.critical,
          ),
          android: AndroidNotificationDetails(
            'high_importance_channel',
            'High Importance Notifications',
            channelDescription:
                'This channel is used for important notifications.',
            channelShowBadge: true,
            icon: "drawable/splash_icon",
            priority: Priority.high,
            importance: Importance.high,
            visibility: NotificationVisibility.public,
            color: ColorPalletes.mainColor,
            enableVibration: true,
            playSound: true,
            styleInformation: BigTextStyleInformation(''),
          ),
        ),
        payload: jsonEncode({
          "subject": {
            "title": message.data["title"] ?? notification?.title ?? ""
          },
          "payload": message.data
        }),
      );
    }
  }
}

as my app delegate file

Appdelagate file

import Flutter
import UIKit
import FirebaseCore
import flutter_local_notifications

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // This is required to make any communication available in the action isolate.
    FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
        GeneratedPluginRegistrant.register(with: registry)
    }

    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

    FirebaseApp.configure()
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}
sanket-mane-17 commented 2 months ago

I'm also facing same issue. For me it's working on android device after updating the package version to 17.2.2.

Uvais-Mohammad commented 2 months ago

Same issue here.

Erchil66 commented 2 months ago

@MaikuB can you help us? on This?

1AhmedYasser commented 1 month ago
override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
   super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
}

override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
   super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
}

Add These to your AppDelegate.swift and it should work

sanket-mane-17 commented 1 month ago

@1AhmedYasser Now it's working, thanks for the help.

Uvais-Mohammad commented 1 month ago

@1AhmedYasser Thanks for your great help. Now its working fine for me.

Erchil66 commented 1 month ago

Yes, Same for me, Kinda need to update the doc, This might also need for further configuration for IOS.

Thanks @1AhmedYasser ,

Erchil66 commented 1 month ago

@1AhmedYasser would like to have some another help also,

as my current appdelegate

import Flutter
import UIKit
import FirebaseCore
import flutter_local_notifications

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    // This is required to make any communication available in the action isolate.
    FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
        GeneratedPluginRegistrant.register(with: registry)
    }

    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

    FirebaseApp.configure()
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
  }    
  override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
  }
}

This works on debug but on release it's just do nothing again, Any idea?

1AhmedYasser commented 1 month ago

@1AhmedYasser would like to have some another help also,

as my current appdelegate

This works on debug but on release it's just do nothing again, Any idea?

I Checked on my side from debug and release using flutter run --debug & flutter run --release and it worked on both

here is my AppDelegate.swift

import UIKit
import Flutter
import CleverTapSDK
import clevertap_plugin

@main
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
        }

        CleverTap.autoIntegrate()
        CleverTapPlugin.sharedInstance()?.applicationDidLaunch(options: launchOptions)

        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: "iOSChannel",
                                           binaryMessenger: controller.binaryMessenger)

        channel.setMethodCallHandler {(call: FlutterMethodCall, result: FlutterResult) -> Void in

            if (call.method == "activityIndicator") {
                let status = call.arguments as? Bool
                if  (status == true) {
                    print("show")
                    UIApplication.shared.isNetworkActivityIndicatorVisible = true
                } else {
                    print("hide")
                    UIApplication.shared.isNetworkActivityIndicatorVisible = false
                }
                return
            }
        }

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
    }

    override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
    }
}

Also make sure the configurations are correct

Screenshot 2024-09-21 at 4 32 34 PM

Version status

Flutter 3.24.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 2663184aa7 (10 days ago) • 2024-09-11 16:27:48 -0500
Engine • revision 36335019a8
Tools • Dart 3.5.3 • DevTools 2.37.3
Erchil66 commented 1 month ago

Am gonna close this ticket

@1AhmedYasser again thank you.

i solved mine by adding the body notification, Am only passing data, So in this kinda weird due when not included the notifcation field the notification can't be tap

sample body sending message:

{
    "message": {
        "token": "token_user",
   /// If i remove notification body the notification heads-up tapping it, Won't work.
        "notification":{
       "title":"Some Title",
       "body":"Some long body description"
     },
        "data": {
            "id": "122",
            "type": "message"
        }
    }
}

As my current appdelegate:

import Flutter
import UIKit
import FirebaseCore
import flutter_local_notifications
// Added some import
import UserNotifications

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    if #available(iOS 10.0, *) {
      UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }

    FirebaseApp.configure()
  // Added some code ----> START
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in }
    /// END <-----
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    super.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
  }    
  override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    super.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
  }
}