Skip to content

[iOS] FlutterLocalNotificationsPlugin.show is not working when setForegroundNotificationPresentationOptions(alert: false) in FirebaseMessaging #2583

@choi88andys

Description

@choi88andys

Describe

I am trying to show a heads-up push notification locally based on certain conditions. This works as expected on Android but not on iOS.

On iOS:

If I set alert: true in setForegroundNotificationPresentationOptions, the notification appears twice (one from the system and one from my local logic).
If I set alert: false or omit setForegroundNotificationPresentationOptions, the push notification never appears.

I checked including #2474 and #2196. They contain relevant information but don’t fully resolve this issue. Please let me know if I missed something.

Question

@MaikuB
Is this the intended behavior on iOS, or is it a bug?
If this is expected, is creating a custom push alert the only way to show a heads-up push message?

To Reproduce

  1. Use Firebase Cloud Messaging (FCM) to receive push notifications.
  2. Set setForegroundNotificationPresentationOptions(alert: false, badge: true, sound: true).
  3. Listen to FirebaseMessaging.onMessage and show a local notification conditionally.
  4. Observe that no notification appears in the foreground.
  5. Set alert: true and observe that notifications appear twice.

Expected behavior

  • I expect to be able to show or hide the push notification conditionally when the app is in the foreground.
  • On iOS, setting alert: false completely prevents the notification from appearing, while setting alert: true always shows it.

Environment

  • Flutter version: 3.27.1
  • flutter_local_notifications version: 17.2.4, 19.0.0
  • firebase_messaging version: 15.1.3, 15.2.4
  • Device 1: iPhone 13 Pro, iOS 18.3.2
  • Device 2: iPhone 13, iOS 18.0

Sample code

// Before runApp
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await setupFlutterNotifications();
await FirebaseMessaging.instance.setAutoInitEnabled(true);
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
  await showFlutterNotification(message);
});
await configPushCondition();
// Configuration
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
late AndroidNotificationChannel channel;
bool isFlutterLocalNotificationsInitialized = false;

Future<void> setupFlutterNotifications() async {
  if (isFlutterLocalNotificationsInitialized) return;

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

  channel = const AndroidNotificationChannel(
    'max_importance_channel',
    'max Importance Notifications',
    description: 'This channel is used for important notifications.',
    importance: Importance.max,
  );

  flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
      ?.createNotificationChannel(channel);
  await flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
          IOSFlutterLocalNotificationsPlugin>()
      ?.requestPermissions(
        alert: true,
        badge: true,
        sound: true,
      );

  const initializationSettings = InitializationSettings(
    android: AndroidInitializationSettings('@mipmap/launcher_icon'),
    iOS: DarwinInitializationSettings(),
  );

  await flutterLocalNotificationsPlugin.initialize(
    initializationSettings,
    onDidReceiveNotificationResponse: (NotificationResponse response) {
      _handleLocalNotificationTap(response.payload);
    },
  );

  isFlutterLocalNotificationsInitialized = true;
}

Future<void> showFlutterNotification(RemoteMessage message) async {
  final RemoteNotification? notification = message.notification;
  final AndroidNotification? android = message.notification?.android;

  if (notification != null && android == null) {
    await flutterLocalNotificationsPlugin.show(
      notification.hashCode,
      notification.title,
      notification.body,
      const NotificationDetails(
        iOS: DarwinNotificationDetails(
          presentAlert: true,
          presentBadge: true,
          presentBanner: true,
          presentSound: true,
          presentList: true,
          interruptionLevel: InterruptionLevel.timeSensitive,
        ),
      ),
    );
  }
}

Future<void> configPushCondition() async {
  final fcm = FirebaseMessaging.instance;
  await fcm.requestPermission();

  if (Platform.isIOS) {
    // return when Simulator

    var apnsToken = await fcm.getAPNSToken();
    if (apnsToken == null) {
      await Future<void>.delayed(const Duration(seconds: 1));
      apnsToken = await fcm.getAPNSToken();
      if (apnsToken == null) {
        print(
          'Can not subscribe topic on this launch by getAPNSToken failed',
        );
        return;
      }
    }
  }

  print('fcmToken: ${await fcm.getToken()}');
}

@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions