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


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

0
Mar 18 at 10:09 AM
User AvatarUmang
#android#flutter#firebase-cloud-messaging

No answer found for this question yet.