flutterdartfirebase-cloud-messagingsharedpreferences

Flutter Firebase Notification Issue: Article Opens Again After Restarting the App


I’m encountering an issue with Firebase Cloud Messaging (FCM) notifications in my Flutter app. Specifically, when opening an article from a notification, closing the app, and then reopening it manually, the app reopens the same article instead of starting normally.

  1. The app is in the background.
  2. A push notification is received for a new article.
  3. The user taps the notification, and the app opens the article correctly.
  4. The user closes the app completely (terminating it).
  5. The user reopens the app manually, without clicking on a notification.
  6. Unexpected behavior: The app still opens the same article from the previous notification.

When reopening the app manually, it should start on the home screen (or the default screen) and not automatically navigate to the last article from the previous notification.

Investigation and Possible Causes

Handling Notification Clicks in Background

FirebaseMessaging.onMessageOpenedApp.listen((message) async {
  print('\n🔔 Notification opened from background:');
  print('📦 Message details:');
  print('ID: ${message.messageId}');
  print('Data: ${message.data}');

  if (message.data['post_id'] != null) {
    print('✅ Post ID found: ${message.data['post_id']}');
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('pending_article_id', message.data['post_id']);
    print('✅ Post ID saved for later opening');
  }
});

Handling App Start with Pending Notifications

Future<String?> checkPendingArticle() async {
  final prefs = await SharedPreferences.getInstance();
  final pendingArticleId = prefs.getString('pending_article_id');

  if (pendingArticleId != null) {
    await prefs.remove('pending_article_id');
    return pendingArticleId;
  }
  return null;
}

Opening the Article if a Pending ID Exists

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Check if the app was launched from a notification
  FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
    if (message.data['post_id'] != null) {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('pending_article_id', message.data['post_id']);
    }
  });

  // Initialize notification controller
  await NotificationController().initialize();

  runApp(
    ChangeNotifierProvider(
      create: (_) => ThemeProvider(),
      child: const MyApp(),
    ),
  );
}

How can I ensure that when reopening the app manually, it does not automatically navigate to the last opened article from the notification? Could this be an issue with how FirebaseMessaging.instance.getInitialMessage() is handled, and if so, what is the best way to reset the stored article ID properly?

Any insights or suggestions on handling this behavior correctly would be greatly appreciated!


Solution

  • You do not need to store the post_id in preferences, If you need it somewhere else during the app session, consider storing it using Provider or any other state management you might be using.

    Your initialization should go like this

    Future<void> initNotification() {
        
     // Triggered when app is opened from background on notification tap
     FirebaseMessaging.onMessageOpenedApp.listen((message) {
      final postId = message.data['post_id'] as String?
      if (postId != null) {
       _handlePostIdNotification(postId);
      }
     }):
    
     // Triggered when notification is tapped when in foreground
     FirebaseMessaging.onMessagelisten((message) {
      final postId = message.data['post_id'] as String?
       if (postId != null) {
        _handlePostIdNotification(postId);
       }
     }):
    
    
     // Returns a remote message if the app was started by notification tap
     final message = await FirebaseMessaging.instance.getInitialMessage(); 
     final postId = message.data['post_id'] as String?
     if (postId != null) {
      _handlePostIdNotification(postId);
     }
    }
    

    You should call initNotification() in the initState of a widget below your ChangeNotifierProvider, so you can use provider to keep postId in memory