[SOLVED] flutter firebase: streams interfering with one another

Issue

This Content is from Stack Overflow. Question asked by Ruder Buster

Im listening to a stream using a bloc. When a user clicks on a chat, the following function is called:

Provider.of<MessageBloc>(context, listen: false).add(LoadMessage(
        chat.stingrayId!, chat.chatId, chat.matchedUserImageUrl!,
        chat: chat,
        stingrayIndex: stingrayIndex,
        matchedUserId: chat.matchedUserId!));

which leads to:

void _onLoadMessage(
    LoadMessage event,
    Emitter<MessageState> emit,
  ) {
    _firestoreRepository
        .messages(event.stingrayId, event.messageId)
        .listen((messages) {
      _firestoreRepository.getUser(event.matchedUserId).listen((matchedUser) {
        add(
          UpdateMessage(
              messages: messages.messages,
              matchedUserImageUrls: event.matchedUserImageUrls,
              chat: event.chat,
              matchedUser: matchedUser,
              blocked: messages.blocked,
              blockerName: messages.blockerName,
              blockerId: messages.blockerId,
              stingrayIndex: event.stingrayIndex),
        );
      });
    });
  }

and the problem stream it listens to is:

Stream<MessagesWithBlock> messages(String stingrayId, String messageId) {
    return FirebaseFirestore.instance
        .collection('stingrayMessages')
        .doc(messageId)
        .snapshots()
        .map<MessagesWithBlock>((doc) => MessagesWithBlock.fromSnapshot(doc));
  }

For some reason, the error seems to be that users in a chat screen will have their message state changed to a different one when another user sends a message in a different chat. This is a bizarre error, as the loadMessage function is only called once when a chat is loaded, but somehow it seems to be reading a different chat id from a different chat. Any ideas on what is going on?

Thanks!



Solution

This is a common mistake when working with streams – forgetting to close the stream (or cancel a listener) when it’s no longer needed.

In your widget, you need to keep a reference to the listener (presumably some kind of StreamSubscription) and then once the user has left that page, cancel the listener in the widget’s dispose function:

late StreamSubscription _listener;

void _onLoadMessage(
    LoadMessage event,
    Emitter<MessageState> emit,
  ) {
    _listener = _firestoreRepository
      .messages(event.stingrayId, event.messageId)
      .listen((messages) {
    ...
    });
}

@override
void dispose() {
    _listener.cancel();
    super.dispose();
}


This Question was asked in StackOverflow by Ruder Buster and Answered by Nick Fisher It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?