1
votes

My Flutter app has multiple BloCs (via the bloc and flutter_bloc packages) which has caused some technical difficulties that I solved using a workaround, but I was wondering if there was a better solution out there.

I am using BlocBuilder() when listening to a bloc where each bloc has its own BlocBuilder() call. At first, I was nesting the BlocBuilder() calls as follows:

Widget build(BuildContext context) {
    return BlocBuilder (
           
       bloc: bloc1,
            
       builder: (ctx, snapshot1) {
          do_something1(snapshot1);
                
          return BlocBuilder(ctx2, snapshot2) {
             bloc: bloc2,
             builder: (ctx2, snapshot2) {
                       
                do_something2(snapshot2);
                      
                return renderWidget();
             }
             
          }
            
       }
       
    );
    
}

The issue I have with this nested BlocBuilder() calls is that if data comes in for bloc1, the BlocBuilder() for bloc2 will be re-called, causing the current state for bloc2 to be re-read() and causing difficulties for do_something2() which ideally should be called only when there is new data for bloc2.

So what I did was to create a separate widget for each BlocBuilder() call, resulting with the following new build():

Widget build(BuildContext context) {
    return Column(
         
       children: [
          WidgetBlocBuilder1(),
                
          WidgetBlocBuilder2(),
          renderWidget(),
        ],
    
    );
}

What this does is that any data coming in for either bloc1 or bloc2 would be localized in WidgetBlocBuilder1() or WidgetBlocBuilder2() respectively and more importantly, incoming bloc data would NOT cause the BlocBuilder() for the other bloc to be re-called() as was the case in my nested BlocBuilder() approach.

Here is the build() for WidgetBlocBuilder1():



Widget build(BuildContext context) {
    return BlocBuilder(
      bloc: bloc,
      builder: (ctx, snapshot) {
        if (snapshot is CommandEditorSaveFile) {
          _saveDocumentFile(ctx);
        }
        return Visibility(
          child: Text('test'),
          visible: false,
        );
      },
    );
}

Note that WidgetBlocBuilder1() is an invisible widget as shown by the Visibility( visible:false) wrapper. This is because the widget itself should not render anything on the screen; the widget is simply a wrapper for the BlocBuilder() call. If incoming bloc data is supposed to change the visible state of the parent widget, then logic needs to be written to implement that.

This workaround seems to work, but I was wondering if there was a better solution out there.

Any suggestions would be greatly appreciated.

/Joselito

1
cannot you use one StreamBuilder instead of two BlocBuilders?pskink
pskink, thanks for the response, but I'd rather keep to the amount of blocs that I currently have, which is two. I could switch to streams instead of blocs, but that would still leave me with 2 streams to listen to and therefore requiring 2 StreamBuilders(). If I nest the calling of this StreamBuilders(), I would end up with the same problem I began with.Jose
"I would end up with the same problem I began with." - no, i meant you still have two Blocs and you show the combined data using one StreamBuilderpskink
pskink, I wasn't aware that was possible. Let me look into it and get back to you.Jose
pskink, I was able to listen to multiple blocs using one StreamBuilder as per your suggestion. I used the multiple_streambuilder package. Thanks.Jose

1 Answers

1
votes

As per a suggestion from pskink, another solution is to use one StreamBuilder() to listen to multiple blocs. To do this, I used a package called multiple_streambuilder.

Here is a sample build() using this package:

  Widget build(BuildContext context) {
    return StreamBuilder2<int, int>(
      streams: Tuple2(bloc1!.stream, bloc2!.stream),
      builder: (contex, snapshot) {
        if (snapshot.item1.hasData) {
          print(snapshot.item1.data);
        }
        if (snapshot.item2.hasData) {
          print(snapshot.item2.data);
        }
        return Scaffold(
          appBar: AppBar(title: Text("test title")),
          body: WidgetTest(),
        );
      },
    );
 }

In the build() above, I was listening to 2 blocs, both returning an int hence the call to StreamBuilder2<int,int>(). To find out which bloc has emitted data, you call snapshot.item1.hasdata or snapshot.item2.hasdata.