Search code examples
flutterblocrxdart

Is nested StreamBuilder good pattern?


I am fetching articles from HackerNews API using Bloc Pattern and Streams.

I am loading all the articles and presenting in the UI with the help of a stream builder, and this works fine.

Now I wrapped the article fetching Stream builder with the new loading StreamBuilder. Now when the loading stream builder has true (means it is loading) it shows a circular progress indicator or else, it shows the child (Article List wrapped with a Stream Builder).

This works fine. But it is bugging me that I have wrapped Stream builder inside a stream builder. I know I can take help of rxdart but I am just not sure how.

I tried to add a loader with the help of snapshot.hasData or not but that didn't work, so I decided to create another stream and subject that takes a bool and tells the UI if it is loading or not.

Code fetching data int the bloc:

 _getAndUpdateArticles(StoryType storyType) {
    _isLoadingSubject.add(true);
    getListIds(storyType).then((list) {
      getArticles(list.sublist(0, 10)).then((_){
        _articleSubject.add(UnmodifiableListView(_articles));
        _isLoadingSubject.add(false);
      });
    });
  }

UI:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: StreamBuilder(
        stream: widget.hnBloc.isLoading,
        builder: (context, snapshot) {
          if (snapshot.data) {
            return Center(child: CircularProgressIndicator());
          } else {
            return StreamBuilder<UnmodifiableListView<Article>> (
              initialData: UnmodifiableListView<Article>([]),
              stream: widget.hnBloc.article,
              builder: (context, snapshot) => ListView(
                children: snapshot.data.map(_buildItem).toList(),
              ),
            );
          }
        },
      ),
  .........

EDIT

I have tried this, but this isn't working:

StreamBuilder<UnmodifiableListView<Article>> (
        initialData: UnmodifiableListView<Article>([]),
        stream: widget.hnBloc.article,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView(
              children: snapshot.data.map(_buildItem).toList(),
            );
          } else {
            return CircularProgressIndicator();
          }
        }
      ),

Solution

  • I Don't think there is a complete way to avoid nested StreamBuilders. I personally wouldn't consider it a bad practice, but it will definitely lead to more build.
    In your case, You can modify your hnBloc to emit a single state that can be a loading state or data state , thereby eliminating the need for a nested StreamBuider.

    eg.

    StreamBuilder<HnState>(
              stream: hnBloc.currentState,
              initialData: HnLoadingState(),
              builder: (context, snapshot) {
                if (snapshot.data is HnLoadingState) {
                  return Center(child: CircularProgressIndicator());
                }if (snapshot.data is HnDataState) {
                  return ListView(
                    children: snapshot.data.map(_buildItem).toList(),
                  ),
                }
    
              },
    )  
    

    This pattern is very common when using the flutter_bloc package. You can see a basic example of this here to understand it better.