Is nested StreamBuilder good pattern?
Asked Answered
C

1

12

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();
          }
        }
      ),
Chantay answered 6/5, 2019 at 20:58 Comment(0)
S
8

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.

Sorrell answered 6/5, 2019 at 22:23 Comment(2)
Thanks for your response @Sorrell but I tried this way but the progress bar isn't showing up. The old data stays on the screen and so the circularprogressbar doesn't come up. I have edited the question to show what I have done.Chantay
That is not the pattern used in the link I provided but if you do it this way, you have to remove your initialData so that the progress can display when there is no data and you also have to add null to the article subject anytime you want to update to clear the old data. I don't think you should worry about your nested StreamBuilder though.Sorrell

© 2022 - 2024 — McMap. All rights reserved.