firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.71k stars 3.98k forks source link

onMessage.Listen is not always triggered #13276

Closed USER4247 closed 2 months ago

USER4247 commented 2 months ago

Is there an existing issue for this?

Which plugins are affected?

No response

Which platforms are affected?

Android

Description

I am making a chat application which heavily relies on fcm messaging . I have recently noticed that all background messages are being stored ie all messages received in background are being stored successfully , but foreground messages sometimes work and sometimes dont work . onMessage.listen is not always triggered due to which there is a huge skip in my messages .

I cannot really showcase the issue since issue is realtime and also there are no bugs/errors associated to my code . I tried manipulating FirebaseMessagingReciever code but I cannot find any way to manipulate it . I am trying to remove return; statment from if-else block which controls foreground and background messages since backgroundMessages are always triggered . If there's a fix to this please suggest a fix or please guide me on how can I make proposed changes to my code .

Reproducing the issue

1 make a flutter app with 2-3 nested screens 2 add foreground and background listener on main method 3 try multiple fcm messages (mine are all data messages) 4 use recent flutter version as of now

Firebase Core version

3.3.0

Flutter Version

3.24.0

Relevant Log Output

No response

Flutter dependencies

Expand Flutter dependencies snippet
```yaml Replace this line with the contents of your `flutter pub deps -- --style=compact`. ```

Additional context and comments

No response

Here is sample code

import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'dart:developer';

import 'package:unilinkd/firebase_options.dart';

import 'scripts/my_firebase_messaging_service.dart'; // For logging

void main() async {
    //try 
    //{ pendingCheck(); } catch (e) {}
    //checkNotificationBuffer();
    //final String appDocDir = getAppDataDir();
    //final String credJsonPath = "$appDocDir/cred2.json";
    //final store = JsonStore(credJsonPath);
    //var parent = store.get('UserName');
    //log("HERE IS NAME OF USER $parent");
    //CheckProfilePicDir();

    WidgetsFlutterBinding.ensureInitialized();
    //await _requestPermissions();
    //await requestExternalAccess();
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
     FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
      print("Hey I am triggered from foreground");
    }); 
    FirebaseMessaging.onBackgroundMessage(onBackgroundHandler);
    await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
    print('Message clicked: ${message.messageId}');
    });
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Chat App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeScreen(),
    );
  }
}

// HomeScreen widget
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();

  static final GlobalKey<_HomeScreenState> homeScreenKey = GlobalKey<_HomeScreenState>();

  void refresh() {
    log("HomeScreen refresh initiated!");
    if (homeScreenKey.currentState != null) {
      homeScreenKey.currentState!.refreshHome();
    }
  }
}

class _HomeScreenState extends State<HomeScreen> {
  void refreshHome() {
    // Logic to refresh home screen
    log("Home screen refreshed!");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => ChatScreen(
                  uid: '12345',
                  type: 'personal',
                  name: 'John Doe',
                ),
              ),
            );
          },
          child: Text('Go to Chat Screen'),
        ),
      ),
    );
  }
}

class ChatScreen extends StatefulWidget {
  final String uid;
  final String type;
  final String name;

  const ChatScreen({
    Key? key,
    required this.uid,
    required this.type,
    required this.name,
  }) : super(key: key);

  @override
  _ChatScreenState createState() => _ChatScreenState();

  static final GlobalKey<_ChatScreenState> chatScreenKey = GlobalKey<_ChatScreenState>();

  void refresh() {
    log("FCM REFRESH INITIATED !!!!!!!!");
    if (chatScreenKey.currentState != null) {
      chatScreenKey.currentState!.refreshChats();
    }
  }
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _messageController = TextEditingController();
  List<String> _messages = [];

  void refreshChats() {
    // Logic to refresh chat screen
    log("Chat screen refreshed!");
  }

  void _sendMessage() {
    final message = _messageController.text.trim();

    // I have a https firebase function that sends message to my project ....

    if (message.isNotEmpty) {
      setState(() {
        _messages.add(message);
      });
      log('Message sent: $message');
      _messageController.clear(); // Clear the input field
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chat with ${widget.name}'),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(_messages[index]),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _messageController,
                    decoration: InputDecoration(
                      hintText: 'Type your message',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _sendMessage,
                  child: Text('Send'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
Juliusvm commented 2 months ago

I noticed on iOS in version 15.1.0 that

FirebaseMessaging.onMessage.listen((remoteMessage) async {

is not triggered at all anymore. I had to bump down to my previous version 15.0.4

USER4247 commented 2 months ago

I noticed on iOS in version 15.1.0 that

FirebaseMessaging.onMessage.listen((remoteMessage) async {

is not triggered at all anymore. I had to bump down to my previous version 15.0.4

Yeah , I was using the same version , but still the problem persisted . Finally modified the source code under FirebaseMessagingReciever.java . Modified this line in perticular if (FlutterFirebaseMessagingUtils.isApplicationForeground(context)) { FlutterFirebaseRemoteMessageLiveData.getInstance().postRemoteMessage(remoteMessage); } Log.d(TAG,"HEY , I MODIFIED THE CODES SUCCESSFULLY !!!!!!!!!!!!!!"); The line basically helps executing both foreground and backgroud triggers simultaneously . Its surely a workaround since I am using inter process communication using sockets to trigger stuff in other files from background reciever

nuzelac commented 2 months ago

Same here, but not sure if it's a collision with some other plugin I use in the app.

My onMessage is not firing for foreground data notifications in iOS. I can see the notification in Console.app logs and also new log output from FirebaseMessaging but it looks like the data notification is not passed to didReceiveRemoteNotification in iOS from what I could debug. I had to disable swizzle and then it started working but I know that is frowned upon :)

I have no idea when this started breaking but it used to work correctly, I updated a lot of packages in the meantime.

SelaseKay commented 2 months ago

Hi @USER4247 , thanks for the report. I'm unable to reproduce this issue. I fired a couple of test messages while the app was in foreground and they were all received. A little more context on the conditions required for reproducing this issue will be of help.

USER4247 commented 2 months ago

Hi @USER4247 , thanks for the report. I'm unable to reproduce this issue. I fired a couple of test messages while the app was in foreground and they were all received. A little more context on the conditions required for reproducing this issue will be of help.

For testing purpose , I am echoing messages back to my device . On cloud console , all fcm messages were recieved and sent back successfully . I have 2 nested screens within my app . I have defined all firebase configs in main method of my app .

The error is very unpredictable since it sometimes trigger foreground and sometimes does not . I am trying a bunch of things , downgrade flutter . I will try to create a new project and test if the problem persists

Another weird things I found was if I change screen back to the chat screen , messages do echo back , but after some time they don't .

SelaseKay commented 2 months ago

Kindly share a full sample code reproducing the issue.

USER4247 commented 2 months ago

Kindly share a full sample code reproducing the issue.

Hi , I have added example code above . Please note that I am using purely data/silent notification messages . All modules used are set to recent versions . I have also observed that the ```<receiver android:name=".FlutterFirebaseMessagingReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">

</receiver>``` is not  triggered sometimes causing foreground functions not working all the time . 
USER4247 commented 2 months ago

Edit ... I found the issue . I have been using firebase functions for all my messaging . It turns out there is some issue with sendMulticast . Its deprecated and will sometimes work and sometimes will not prompting "UNIMPLEMENTED" error message . I had to change sendMulticast to sendEachForMulticast in my index.js firebase functions . Thank you