1
votes

I have a Flutter project that uses Cloud Firestore. In one of my widgets, I have a StreamBuilder that ideally reads snapshots from the database. In theory, my API for reading from a remote server should be abstract enough to swap Firestore out with a different implementation.

class Database {
  Stream<QuerySnapshot> get snapshots => Firestore.instance.collection('entires').snapshots();

  Stream<List<String>> get entries => snapshots.map((snapshot) => snapshot.documents.map((document) => document.data['name']).toList());
}

If my StreamBuilder uses snapshots, then AsyncSnapshot<QuerySnapshot> has data (hasData returns true).

If my StreamBuilder uses entries, then AsyncSnapshot<List<String>> will never have data (hasData returns false)---even if I successfully printed out what the data is (my return result is a populated list of strings).

I hope to keep my Database interface free of Firestore. So, my question is: why does StreamBuilder's AsyncSnapshot return nothing even if I have data?

1

1 Answers

0
votes

It seems that the issue revolves around snapshot() sending data off immediately. I managed to create a work around by creating a class that wraps my Firestore collection.

This wrapper has its own StreamController<T> where T is the non-Firestore (abstract across multiple backend implementations) data type that I want to return.

The wrapper will listen on the necessary Firestore snapshots on construction. The latest snapshot result is cached. My StreamBuilder can then use both the internal StreamController.stream and the cached snapshot data for its construction.

StreamBuilder(
  stream: _wrapper.stream,
  initialData: _wrapper.latestData,
)

This works for now, but has the side effect of continuously listening on snapshots indefinitely. I'll have to update my wrapper to stop listening on snapshots when not necessary (no subscribers) and re-listen when there are new subscribers, or simply use a mapping of the snapshot stream.