How do you combine 2 Riverpod StreamProviders where 1 stream depends on the data from another stream?

Issue

Sometimes I think I’m getting the logic of providers and then I get stumped for hours trying to do something like below.

I need to get a List of connection id’s from a firestore collectionsstream. Easy.

However, I need to feed this streaming list of connection id’s into another firestore collectionstream. Below, you can see my upcomingEventsStreamProvider is ref.watch both the database and the connectionsStream. There are no errors thrown by riverpod or firestore. However, in the logs, I see my print statements in this order:

returned data
returned null stream value

How am I abusing the power of Riverpod providers? Lol.

final connectionsStreamProvider = StreamProvider<List<UidConnections>>((ref) {
  final database = ref.watch(databaseProvider);
  return database != null ? database.connectionsStream() : Stream.value(null);
});

final connectionsListStateProvider = StateProvider<List>((ref) => []);

final upcomingEventsStreamProvider = StreamProvider<List<SpecialEvents>>((ref) {
  final database = ref.watch(databaseProvider);
  final connectionsStream = ref.watch(connectionsStreamProvider);
  if (database != null && connectionsStream != null) {
    connectionsStream.whenData((data) {
      if (data != null) {
        data.forEach((event) {
          if (event?.active == true && event?.connectedUid != null) {
            ref
                .read(connectionsListStateProvider)
                .state
                .add(event.connectedUid);
          }
        });
        print('returned data');
        return database.upcomingSpecialEventsStream(
            ref.read(connectionsListStateProvider).state);
      }
    });
  }

  print('returned null stream value');
  return Stream.value(null);
});

Or maybe, do I just need to refactor my Firebase Cloud Firestore query to first obtain the connection id stream? I’d rather use Riverpod because I’ll still need a stream of connection id’s alone.

Solution

Still stumped on how to accomplish combining 2 streams with Riverpod alone. However, I did manage to resolve my issue and here’s info for anyone else that runs into this use case where a stream is dependent on data from another stream.

The solution that worked, and very well I might add, is to use rxdart Rx.combineLatest2. See below. Now Riverpod is happy, and providing state for my combined streams. The connection id’s I needed are now part of my Account model thanks to rxdart. Hope this is helpful to someone out there.

final accountInfoStreamProvider = StreamProvider<Account>((ref) {
  final database = ref.watch(databaseProvider);
  return database != null
      ? AccountInfoModel(database: database).accountInfoStream()
      : Stream.value(null);
});

class AccountInfoModel {
  AccountInfoModel({@required this.database});
  final FirestoreDatabase database;

  Stream<Account> accountInfoStream() {
    return Rx.combineLatest2(
      database.accountStream(),
      database.connectionsStream(),
      (Account account, List<Connections> connections) {
        connections.forEach((connection) {
          account.connections.add(connection.documentId);
        });
        return account;
      },
    );
  }
}

Answered By – Zelf

Answer Checked By – Willingham (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.