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();
}