My Android application is displaying notification twice when app is in terminated state

1 day ago 1
ARTICLE AD BOX

I have developed my application in flutter. iOS worked fine but Android application is displaying notification twice when my application is in terminated state.

I have review that, only Google Play Store application displaying notification twice, I am not able to reproduce the scenario in my debug or release build locally.

Any help/answer is appreciated.

Here is my flutter code

main.dart

GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); void main() async { WidgetsFlutterBinding.ensureInitialized(); await Future.delayed(const Duration(milliseconds: 300)); await Firebase.initializeApp(); FlutterError.onError = (details) { FirebaseCrashlytics.instance.recordFlutterFatalError(details); }; PlatformDispatcher.instance.onError = (error, stack) { FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); return true; }; print("notification main.dart called"); // FirebaseMessaging.onBackgroundMessage(handleBackgroundMessage); await PushNotificationService().initNotifications(); await Get.putAsync(() => StorageService().init()); Get.put(ScrollControllerHelper()); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]).then((_) { runApp(MyApp()); }); } class MyApp extends StatelessWidget { final CheckAuthController userController = Get.put(CheckAuthController()); MyApp({super.key}); @override Widget build(BuildContext context) { ScreenUtil.init(context); return GetMaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, title: Strings.appTitle, initialRoute: userController.isOnBoardingDisplayed == true ? RouteConstants.dashboard : RouteConstants.onboarding, initialBinding: userController.isOnBoardingDisplayed == true ? DashboardBinding() : OnboardingBindings(), getPages: Pages.getPages, theme: ThemeData( scaffoldBackgroundColor: ColorConstants.appBackgroundColor, ), defaultTransition: Transition.rightToLeft, translations: AppTranslation(), locale: const Locale(Constants.languageGujarati), fallbackLocale: const Locale(Constants.languageGujarati), supportedLocales: const [ Locale(Constants.languageEnglish), Locale(Constants.languageGujarati), Locale(Constants.languageHindi), ], localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], ); } } push_notification_service.dart import 'dart:convert'; import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; import 'package:prime_debut_flutter/routes/routes.dart'; import 'package:prime_debut_flutter/views/dashboard/dashboard_controller.dart'; class PushNotificationService { final Set<String> _handledMessageIds = {}; PushNotificationService._internal(); static final PushNotificationService _instance = PushNotificationService._internal(); factory PushNotificationService() => _instance; final _firebaseMessaging = FirebaseMessaging.instance; bool _isInitialized = false; final androidChannel = const AndroidNotificationChannel( 'android_channel', 'android_notification_channel', description: 'notification', importance: Importance.high); final _localNotifications = FlutterLocalNotificationsPlugin(); void handleMessage(RemoteMessage? message) async { //when app is minimized and notification come and tap print("notification in push service detail = $message"); var messageData = message?.data; print("notification in push service detail = $messageData"); Map? notificationPayload = messageData; print("notification in push service detail = $notificationPayload"); Map? dataPayload = notificationPayload; print("notification in push service detail = $dataPayload"); String? postId = dataPayload?['id']; String? notificationId = dataPayload?['notification_id']; print("notification in push service postId = $postId"); print("notification in push service notificationId = $notificationId"); if (message == null) return; var controller = Get.find<DashboardController>(); controller.postId = postId ?? ""; controller.notificationId = notificationId; controller.getHomePost(isFromNotification: true,isFromReload: false,isForceRefresh: false); controller.apiReadNotification(); } void handleMessageMinimize(RemoteMessage? message) async { //when app is minimized and notification come and tap print("notification in push service detail = $message"); var messageData = message?.data; print("notification in push service detail = $messageData"); Map? notificationPayload = messageData; print("notification in push service detail = $notificationPayload"); Map? dataPayload = notificationPayload; print("notification in push service detail = $dataPayload"); String? postId = dataPayload?['id']; String? notificationId = dataPayload?['notification_id']; print("notification in push service postId = $postId"); if (message == null) return; var controller = Get.find<DashboardController>(); controller.postId = postId ?? ""; controller.notificationId = notificationId; controller.getHomePost(isFromNotification: true, isFromReload: false,isForceRefresh: false); controller.apiReadNotification(); Routes.goToDashboardView(isFromNotification: true, postId: postId,notificationId: notificationId); } Future initLocalNotifications() async { const android = AndroidInitializationSettings('app_logo'); const ios = DarwinInitializationSettings(); const settings = InitializationSettings(android: android, iOS: ios); await _localNotifications.initialize( settings, onDidReceiveNotificationResponse: _onReceived, onDidReceiveBackgroundNotificationResponse: _onReceived, ); final platform = _localNotifications.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>(); await platform?.createNotificationChannel(androidChannel); _localNotifications.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>()?.requestNotificationsPermission(); } Future initPushNotifications() async { await FirebaseMessaging.instance .setForegroundNotificationPresentationOptions( alert: true, badge: true, sound: true); FirebaseMessaging.instance.getInitialMessage().then((message){ print("notification in push service getInitialMessage = $message"); handleMessageMinimize(message); }); FirebaseMessaging.onMessageOpenedApp.listen((message){ print("notification in push service onMessageOpenedApp = $message"); //when app is minimized and notification come and tap handleMessageMinimize(message); }); FirebaseMessaging.onBackgroundMessage(handleBackgroundMessage); print("notification in FirebaseMessaging.onMessage"); FirebaseMessaging.onMessage.listen((message) { final data = message.data; final dedupeKey = (data['notification_id'] ?? message.messageId ?? '${data['id']}_${message.sentTime?.millisecondsSinceEpoch ?? ''}') .toString(); if (dedupeKey.isNotEmpty && _handledMessageIds.contains(dedupeKey)) return; if (dedupeKey.isNotEmpty) _handledMessageIds.add(dedupeKey); final nidStr = (data['notification_id'] ?? '').toString(); final int notifId = int.tryParse(nidStr) ?? (nidStr.isNotEmpty ? nidStr.hashCode : DateTime.now().millisecondsSinceEpoch.remainder(100000)); print("========== FCM ==========notifId = $notifId"); print("messageId: ${message.messageId}"); print("from: ${message.from}"); print("sentTime: ${message.sentTime}"); print("notification in local notification ${message.notification?.title}"); final notification = message.notification; if (notification != null) { _localNotifications.show( notifId, notification.title, notification.body, NotificationDetails( android: AndroidNotificationDetails( androidChannel.id, androidChannel.name, channelDescription: androidChannel.description, icon: 'app_logo',showWhen: false,)), payload: jsonEncode(message.toMap())); } }); } Future<void> initNotifications() async { print("notification initNotifications called"); if (_isInitialized) { print("PushNotificationService already initialized"); return; } _isInitialized = true; await _firebaseMessaging.requestPermission(); String? apnsToken; if (Platform.isMacOS || Platform.isIOS) { apnsToken = await _firebaseMessaging.getAPNSToken(); } if ((Platform.isIOS && apnsToken != null) || Platform.isAndroid) { generateFcmToken(); _firebaseMessaging.subscribeToTopic('local_news_set_up'); print("topic subscribed"); } await initLocalNotifications(); await initPushNotifications(); } generateFcmToken() async{ final fcmToken = await _firebaseMessaging.getToken(); print("Fcm token --- ${fcmToken}"); } void unSubscribe(){ _firebaseMessaging.unsubscribeFromTopic('local_news_set_up'); } } void _onReceived(NotificationResponse details) async { //when app is opened at that time navigation occurred print("notification 1 detail = $details"); var messageData = details.payload; Map notificationPayload = (jsonDecode(messageData!)); Map dataPayload = (notificationPayload["data"]); var postId = dataPayload['id']; String? notificationId = dataPayload['notification_id']; print("Raw payload: $postId and notificationId = $notificationId"); var controller = Get.find<DashboardController>(); controller.postId = postId; controller.notificationId = notificationId; controller.homePostListPage = 1; controller.getHomePost(isFromNotification: true, isFromReload: false,isForceRefresh: false); controller.apiReadNotification(); } Future<void> handleBackgroundMessage(RemoteMessage message) async { print("notification 2 detail = $message"); print("message back-- ${message.notification}"); }
Read Entire Article