Flutter FCM notification onTap callback not working when app is in foreground, works in background

5 hours ago 2
ARTICLE AD BOX

I am working on Flutter push notifications using Firebase Cloud Messaging and local notifications.

The issue is:

When the app is in background or terminated, tapping the notification works correctly.

But when the app is already running in foreground, I receive the notification, but tapping it does nothing.

My log/print statements inside the tap callback are not getting printed.

I am showing notifications manually when the app is in foreground.

Current Behavior

Foreground

Notification appears

Tap callback does NOT trigger

print/log not executed

Background

Notification appears

Tap callback works correctly

Flutter Packages

firebase_messaging: flutter_local_notifications:

Firebase Payload From Backend

{ "message": { "token": "DEVICE_TOKEN", "notification": { "title": "Test Title", "body": "Test Body" }, "data": { "type": "chat", "id": "123", "taks_id: "123", "taskable_id: "123" } } } import 'dart:convert'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:mre/utils/notification_router.dart'; final class LocalNotificationHelper { static final FlutterLocalNotificationsPlugin _plugin = FlutterLocalNotificationsPlugin(); static Future<void> init() async { const android = AndroidInitializationSettings('@mipmap/retiq'); const ios = DarwinInitializationSettings(); const settings = InitializationSettings(android: android, iOS: ios); await _plugin.initialize( settings, onDidReceiveNotificationResponse: (response) { if (response.payload != null) { final data = jsonDecode(response.payload!); NotificationRouter.handle(data); } }, ); await _createChannels(); } static Future<void> _createChannels() async { const channel = AndroidNotificationChannel( 'high_importance_channel', 'High Importance Notifications', importance: Importance.high, enableLights: true, sound: RawResourceAndroidNotificationSound("notification")); await _plugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); } static Future<void> showNotification(RemoteMessage message) async { final data = message.data; final title = data['title'] ?? message.notification?.title; final body = data['body'] ?? message.notification?.body; final androidDetails = AndroidNotificationDetails( 'high_importance_channel', 'High Importance Notifications', importance: Importance.high, priority: Priority.high, ); const iosDetails = DarwinNotificationDetails(); await _plugin.show( DateTime.now().microsecond, title, body, NotificationDetails( android: androidDetails, iOS: iosDetails, ), payload: jsonEncode(data), ); } } import 'dart:convert'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:mre/utils/notification_helper.dart'; import 'package:mre/utils/notification_router.dart'; class NotificationService { static final FirebaseMessaging _messaging = FirebaseMessaging.instance; static String? _lastHandledMessageId; static Future<void> init() async { await _requestPermission(); /// Foreground messages FirebaseMessaging.onMessage.listen(_onForegroundMessage); /// App opened from background FirebaseMessaging.onMessageOpenedApp.listen(_onMessageOpened); /// App opened from terminated state final initialMessage = await _messaging.getInitialMessage(); if (initialMessage != null) { _handleNavigation(initialMessage); } } static Future<void> _requestPermission() async { await _messaging.requestPermission( alert: true, badge: true, sound: true, ); } /// Foreground → show local notification static void _onForegroundMessage(RemoteMessage message) { LocalNotificationHelper.showNotification(message); } /// Background tap static void _onMessageOpened(RemoteMessage message) { _handleNavigation(message); } /// Navigation handler (DEDUPLICATED) static void _handleNavigation(RemoteMessage message) { final messageId = message.messageId ?? jsonEncode(message.data); if (_lastHandledMessageId == messageId) return; _lastHandledMessageId = messageId; NotificationRouter.handle(message.data); } } import 'package:flutter/material.dart'; import 'package:mre/screens/opn_form/view/opn_form_view.dart'; import '../core/mk_route.dart'; import "../utils/context.dart"; class NotificationRouter { static void handle(Map<String, dynamic> data) { final context = GlobalContext.instance.navigatorKey?.currentContext; if (context == null) return; final type = data['type'] ?? ""; switch (type) { case 'POS_ACCESS_REQUEST': Navigator.pushNamed(context, RoutePage.accessRequest); break; case 'TASK': Navigator.of(context).push(MaterialPageRoute( builder: (context) { return OpnFormScreen( taskableId: data["taskable_id"], taskId: data['task_id'], ); }, )); break; } } } Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); /// Disable rotation if (Platform.isIOS || Platform.isAndroid) { SystemChrome.setPreferredOrientations( [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); } runApp(AppRoot()); _initializeAsync(); } void _initializeAsync() async { await FirebaseIntegration().invoke(); await Future.wait([ CrashlyticsIntegration().invoke(), UpGraderIntegration().invoke(), LocalNotificationHelper.init(), NotificationService.init() ]); _requestLocationPermission(); }
Read Entire Article