Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BLoCs and multiple streams - Is there a better solution?

Tags:

flutter

bloc

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.

like image 529
Daniel Avatar asked Jul 12 '19 12:07

Daniel


1 Answers

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.

like image 143
GiancarloCode Avatar answered Nov 09 '22 03:11

GiancarloCode