'Flutter: Get Data from Firebase using Future wait

I'm trying to load real-time data for a chat using BLoC but I'm stumped on how to get this to work right & retrieve data.

The goal is to build this using BLoC so I can load in messages right away, handle pagination to bring in more messages, and reload if a message is deleted, flagged, etc.

I'm always hitting my empty state. The return is called right away but the await does run and I can log out that I am getting all my messages. I just don't know how to return once all of that is complete so I can display them for a user.

Here is what I have tried:

Future _loadChatMessages(
      DatabaseReference ref, int numberOfMessages) async {
   
    // Number of chats...
    numberOfChatsToDisplay = numberOfChatsToDisplay + numberOfMessages;

    // clear our messages...
    chatMessages.clear();

    // get our messages...
    ref.orderByKey().limitToLast(numberOfChatsToDisplay).onValue.listen(
      (event) async {
        final firebaseData = Map<String, dynamic>.from(event.snapshot.value);

        await Future.wait(
          firebaseData.values.map(
            (message) async {
              var shouldAdd = true;

              final mappedMessage = Map<String, dynamic>.from(message);

              // check if message was flagged
              shouldAdd = false;

              // check if user was blocked
              shouldAdd = false;

              // create new message
              final newMessage = Message.fromJson(mappedMessage);
              if (shouldAdd) {
                chatMessages.add(newMessage);
              }
            },
          ),
        );
      },
    );
    // This just gets called right away and is empty, never called again
    return chatMessages;
  }


Solution 1:[1]

The onValue property is a stream, which immediately fires an event with the current data from the database, and then again whenever there's a change to the database. Since there may be multiple events, you can meaningfully use await when listening to a stream.

If you do want the stream, there's simply no way to (meaningfully)nreturn a single value.

If you only want one value, use once instead of onValue:

Future _loadChatMessages(DatabaseReference ref, int numberOfMessages) async {
  numberOfChatsToDisplay = numberOfChatsToDisplay + numberOfMessages;

  chatMessages.clear();

  var snapshot = await ref.orderByKey().limitToLast(numberOfChatsToDisplay).once();
  final firebaseData = Map<String, dynamic>.from(snapshot.value);

  firebaseData.values.map((message) {
    var shouldAdd = true;

    final mappedMessage = Map<String, dynamic>.from(message);

    shouldAdd = false;
    shouldAdd = false;

    final newMessage = Message.fromJson(mappedMessage);
    if (shouldAdd) {
      chatMessages.add(newMessage);
    }
  });
  return chatMessages;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Frank van Puffelen