BLoCs and multiple streams - Is there a better solution?
Asked Answered
C

1

7

Currently I'm working with BLoCs in Flutter and I've got a question about multiple streams within a Bloc.

For example when a screen has got multiple widgets which should depend on the Bloc. I could wrap the whole screen in the StreamBuilder, but then every time all widgets would be rebuilt.

The example bloc:

class TestBloc {
  final StreamController _dataController = StreamController<String>();
  final StreamController _appBarTitleController = StreamController<String>();

  TestBloc();

  Stream<String> get appBarTitle => _appBarTitleController.stream;

  Stream<DataState> get data => _dataController.stream;

  void fetchData(String path) async {
    _dataController.sink.add(PokemonDataLoading());

    Data data = await _getData();

    _dataController.sink.add(Loaded(data));
    _appBarTitleController.sink.add(data.name);
  }

  Future<Data> _getData(String path) async {
    return await _dataRepository.fetchData(path);
  }

  void dispose() {
    _dataController.close();
    _appBarTitleController.close();
  }
}

On the example build method you can see two different StreamBuilders, one for the app bar title and one for the content. Of course I could wrap them in this example into one StreamBuilder, but sometimes this isn't easily possible. They may depend on other data or user interactions.

@override
Widget build(BuildContext context) {
  _testBloc.fetchData();

  return ScaffoldWithSafeArea(
    title: StreamBuilder(
      stream: _testBloc.appBarTitle,
      builder: (context, AsyncSnapshot<String> snapshot) {
        if (snapshot.hasData) {
          return Text(snapshot.data);
        }
        return Text("Test");
      },
    ),
    child: StreamBuilder<DataState>(
      stream: _testBloc.data,
      builder: (context, AsyncSnapshot<DataState> snapshot) {
        DataState state = snapshot.data;

        if (state is DataInitial) {
          return _buildLoading();
        } else if (state is DataLoaded) {
          return _buildContent(state.data);
        }
        return _buildLoading();
      },
    ),
  );
}

Is there maybe a better solution for multiple Streams on one screen? I use a lot of boilerplate code here and would like to avoid this.

Colman answered 12/7, 2019 at 12:13 Comment(0)
A
3

In order to manage multiple streams in one screen, the best solution is to have multiple widgets that listen the corresponding stream.

This way, you increase the performance of your app by optimizing the total number of builds of your widgets.

By doing this, you can create widgets that listen an output (stream) of your BLoC and reuse them in different parts of your app, but in order to make the widget reusable you need to inject the BLoC into the widget.

If you see the BLoC UI design guidelines

Each "complex enough" component has a corresponding BLoC

This way your screen will now be composed of different components, and this component is a widget that listens to an output (stream) of your BLoC.

So you are doing things right.

If you want to reduce the repetitive code a bit in your widgets, you can:

  • Create your own widget that listens to the stream and directly returns the output of the BLoC (in your case you call state), this way you don't need to use snapshot.data like in the StreamBuilder. The example of this widget is the BlocBuilder of flutter bloc library.

  • Use the flutter bloc library that has widgets that reduce the boilerplate code when use BLoC pattern, but if you use this library, you now need to create your BLoCs using bloc library, but if you do this, now you reduce the boilerplate code of creating StreamControllers in your BLoC, and other interesting features, so you should take a look the power of bloc and flutter bloc libraries.

About answered 13/7, 2019 at 6:34 Comment(4)
Thank you for the answer. I tried flutter_bloc and I like it at first glance. However, this requires even more boilerplate code, as I always have to create new events. The events separate the logic again, but this is extensive for many smaller blocs. In addition, I'm not sure how I can implement the above example with flutter_bloc. As far as I can see, I would need for the AppBar an own Bloc and for the content an other one. I can use the Bloc Builder for the same bloc only once, since there is only one mapEventToState method.Colman
Hello, you don't need to create other bloc for this case, your state can have the data and title property, or in your case you can get the title from data property. now you can use 2 BlocBuilder, and if you want to optimized the builds of title and data BlocBuilder, you need to pass function to condition parameter, for example condition: (previousState, currentState) => previousState.title != currenState.title; See: https://mcmap.net/q/1626713/-can-blocbuilder-of-flutter_bloc-avoid-rebuild-part-of-widget-which-not-changing .About
Thanks, the conditions are a good option for this. I'm still not sure if I like flutter_bloc more then an own custom StreamBuilder like this one: gist.github.com/DFelten/c00529f9641c738126d1360e692f21e0Colman
@Colman Have you found a solution? I have the same issue, I don't like bloc, but the same time there are not many alternatives. Besides making your own architecture.Nonego

© 2022 - 2024 — McMap. All rights reserved.