I have a dashboard screen where in the initState I am initializing the Firebase notification and local notification. When the app is foregrounded it’s getting the local notification and getting the notification correctly. But when the app is backgrounded then it’s getting two notifications one from the firebaseMessaging and another from the local notification which is kind of duplicate and false. I want to get rid of it and want only the Firebase messaging to work when the app is backgrounded.
Community suggests this might be my solve:
Notification show twice on flutter. But the available solution on that are not for me. If i dont use the navigation from the payload that solves might work but if i dont use message.notification the how can I navigate from notifications? So as per my understanding both are different questions. And more over the notification from the local notification, the payload is coming as empty. But in the second notification the payload is coming with data.
My code snippets:
initState
@override
void initState() {
super.initState();
currentTab = widget.tab;
initFirebaseMessaging();
_selectTab(currentTab);
}
Firebase messaging
initFirebaseMessaging() async {
await firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
final fcm = await firebaseMessaging.getToken();
logger.w("Driver Token: $fcm");
initPushNotification();
initLocalNotifications();
}
Push notifications
Future initPushNotification() async {
///if app is opened from terminated state : onTap actions performed
firebaseMessaging.getInitialMessage().then(
(RemoteMessage? message) {
if (initialRemoteMessage != null) {
RemoteNotification? notification = initialRemoteMessage!.notification;
logger.w(
"Driver title:${notification?.title} Driver data: ${notification?.body}");
logger.w("Driver Data:${message?.data}");
moveScreenFromNotification(initialRemoteMessage!.data);
flutterToast(initialRemoteMessage!.messageType ?? '');
} else if (message != null) {
RemoteNotification? notification = message.notification;
logger.w("RemoteNotification");
logger.w("title:${notification?.title} data: ${notification?.body}");
logger.w("Data:${message.data}");
moveScreenFromNotification(message.data);
flutterToast(message.messageType ?? '');
initialRemoteMessage = message;
}
},
);
//*If app is in background state : actions performed
FirebaseMessaging.onMessageOpenedApp.listen((message) {
moveScreenFromNotification(message.data);
});
//*If app is in background state
FirebaseMessaging.onBackgroundMessage(handleBackGroundMessage);
//* If the app is in foreground state:(Local notifications)
FirebaseMessaging.onMessage.listen((message) {
final notification = message.notification;
if (notification == null) return;
moveScreenFromNotification(message.data);
localNotification.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
androidChannel.id, androidChannel.name)),
payload: jsonEncode(message.toMap()));
});
}
Local notifications
Future initLocalNotifications() async {
const ios = IOSInitializationSettings();
const android = AndroidInitializationSettings("@drawable/logo");
const setting = InitializationSettings(android: android, iOS: ios);
await localNotification.initialize(setting,
onSelectNotification: (payload) {
if (payload?.contains(".pdf") ?? false) {
logger.w("PDF Executing");
OpenFile.open(payload);
} else {
logger.w("Driver Message is Executing");
logger.w("Payload: $payload");
final message = RemoteMessage.fromMap(jsonDecode(payload ?? ""));
logger.w("DATA from local: ${message.data}");
handleBackGroundMessage(message);
moveScreenFromNotification(message.data);
}
});
final platform = localNotification.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
await platform?.createNotificationChannel(androidChannel);
}
handle background message
Future<void> handleBackGroundMessage(RemoteMessage message) async {
RemoteNotification? notification = message.notification;
NotificationService notificationService = NotificationService(channelId: "D");
await notificationService.showNotification(message: message);
flutterToast(notification?.body ?? '');
}
These are all the associated snippets. Is there any way to get one notification while the app is in the background and not use the local notification function?
my manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.xxxxxxxx">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
android:maxSdkVersion="32"/>
<application
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:label="BitesVilla">
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/logo" />
<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GMP_KEY}" />
<activity
android:name=".MainActivity"
android:exported="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<!-- Deep linking -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="bitesvilla.page.link" android:pathPrefix="/"/>
<data android:scheme="http"
android:host="bitesvilla.page.link" android:pathPrefix="/"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver"/>
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
</application>
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
</manifest>
I have tried logging to understand what is going on. But it seems like the way of implementation is causing this. I cannot think of anything better for now as I am kind of new to the local notifications.
8
Try this, I hope it works.
I’ve added a check to only show the local notification when the app is in the foreground
. When the app is in the background
, FCM
will handle the notification and display it to the user.
Future initPushNotification() async {
///If app is opened from terminated state : onTap actions performed
firebaseMessaging.getInitialMessage().then(
(RemoteMessage? message) {
if (initialRemoteMessage!= null) {
RemoteNotification? notification = initialRemoteMessage!.notification;
logger.w(
"Driver title:${notification?.title} Driver data: ${notification?.body}");
logger.w("Driver Data:${message?.data}");
moveScreenFromNotification(initialRemoteMessage!.data);
flutterToast(initialRemoteMessage!.messageType?? '');
} else if (message!= null) {
RemoteNotification? notification = message.notification;
logger.w("RemoteNotification");
logger.w("title:${notification?.title} data: ${notification?.body}");
logger.w("Data:${message.data}");
moveScreenFromNotification(message.data);
flutterToast(message.messageType?? '');
initialRemoteMessage = message;
}
},
);
//*If app is in background state : actions performed
FirebaseMessaging.onMessageOpenedApp.listen((message) {
moveScreenFromNotification(message.data);
});
//*If app is in foreground state:(Local notifications)
FirebaseMessaging.onMessage.listen((message) {
if (message.notification!= null) {
// Show local notification only when app is in foreground
localNotification.show(
message.notification.hashCode,
message.notification.title,
message.notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
androidChannel.id, androidChannel.name)),
payload: jsonEncode(message.toMap()));
}
});
}
1