5

I have recently heard and read a lot about the BLoC architecture and it makes a lot of sense and is similar to program structures I have used before.

However, one thing I have found unclear is the streams that a BLoC should instantiate and make available.

Let us consider that the app user has two buttons, which when pressed will create two distinct objects which need to be handled by distinct methods in the BLoC. I am unsure whether this warrants the creation of two StreamController objects and exposing two sinks, or if they should share a common StreamController and the actions are differentiated between using runtime type checking.

The two approaches are as follows; the first approach creates two stream controllers in the BLoC:

  final _actionOneController = StreamController<ActionOne>();
  StreamSink<ActionOne> get actionOne => _actionOneController.sink;

  final _actionTwoController = StreamController<ActionTwo>();
  StreamSink<ActionTwo> get actionTwo => _actionTwoController.sink;

creates two events by adding them to different sinks:

 Button(child: Text("Action One"), onPressed: () => bloc.actionOne.add(ActionOne(data, data1, data3)),
 Button(child: Text("Action Two"), onPressed: () => bloc.actionTwo.add(ActionTwo(someOtherData)),

and handles them by listening to two streams separately.

The second approach exposes a single StreamSink:

  final _actionController = StreamController<Action>();
  StreamSink<Action> get action => _actionController.sink;

create the two events by adding them to the common sink:

 Button(child: Text("Action One"), onPressed: () => bloc.action.add(ActionOne(data, data1, data3)),
 Button(child: Text("Action Two"), onPressed: () => bloc.action.add(ActionTwo(someOtherData)),

and then differentiate between the actions in the BLoC with a method that checks the runtime type of the Action object:

void _actionListener(Action action){
  if(action is ActionOne)
    actionOneHandler(action);
  if(action is ActionTwo)
    actionTwoHandler(action);
}

I would like to emphasize that the two actions create distinct objects with no common fields i.e. they do not share a commonly inherited ancestor class.

Delgan
  • 18,571
  • 11
  • 90
  • 141
turbo_sheep
  • 109
  • 2
  • 7

1 Answers1

5

As the name say, the core idea behind BLoC is to separate the responsibilities between UI and business rules, so depends on how you want to architect and organize that logic. Maybe if you implement a bloc for a simple screen, you'll be good with one stream, but as your app gets complex maybe you'll need to break those blocs in small blocs with logic that is related with a single object and then compose it for each screen.

To answer the question, the true is that depends of the case, but as you emphasize that the two actions treat distinct objects ill suggest you to handle it in different streams to have a nice separation of responsibilities. In the future, if you need to refactor and maintain the code it will be easier since you already have all the logic completely separated.

Another important advantage of this is that you can have strongly typed Streams with all the benefits that entails, in the second case Actions must be in the same hierarchy in order to be added to the same sink.

Hope it helps!

fvillalba
  • 963
  • 10
  • 18
  • 1
    This was the implementation I went with, motivated by separation of responsibility since the objects were of different types (and having strongly typed streams). – turbo_sheep Dec 02 '19 at 17:34