Closed psygo closed 4 years ago
Hi @psygo ๐ Thanks for opening an issue!
A bloc is essentially a special type of StreamController
which allows you to define a transformation function (mapEventToState
). With a StreamController
the input type has to match the output type.
I highly encourage you to try to accomplish the same thing with StreamControllers
(it's a good learning exercise) and you should see that you'll need multiple controllers to accomplish the same thing.
If you have questions about yield*
I'm happy to try to answer them and improve the docs to make it easier for you as well as the rest of the community.
Let me know if that helps ๐
A bloc is essentially a special type of
StreamController
which allows you to define a transformation function (mapEventToState
).
Knowing that is already really neat. That info could be placed below the Streams Section of the docs for example, I think it would be useful to bring BLoC into "familiar" reactive terms, but that's only my opinion...
I can see from the source code that the BLoC abstract class
is extending and implementing the two important reactive components in Dart also, Stream
and Sink
.
I think it would also be very useful to have some sort of basic sketched UML diagram of the library in the docs, wouldn't it? It would also increase the chances of contributions drastically — is there a package to do this programmatically? Have I skipped this somewhere in the documentation?
With a
StreamController
the input type has to match the output type.
True, and I admit I hadn't paid that much attention to this point. But couldn't you use a StreamTransformer
to not only deal with mapEventToState
but also change the output type? Maybe something like this:
void main() {
final Stream<int> intStream = streamOfInts(10); // would print 0, 1, 2, ..., 10
final StreamTransformer<int, String> streamTransformer =
StreamTransformer<int, String>
.fromHandlers(handleData: (int event, EventSink<String> output)
=> output.add('number: ' + event.toString()));
final Stream<String> stringStream = intStream.transform<String>(streamTransformer);
stringStream.listen(print); // prints number: 0, number: 1, number: 2, ..., number: 10
}
Stream<int> streamOfInts(int intTotal) async* {
for (int i = 0; i <= intTotal; i++) {
yield i;
}
}
It would be even easier with a .map()
actually:
final Stream<String> stringStream =
intStream.map<String>((int i) => 'number: ${i.toString()}');
Hi @psygo ๐
If you take a closer look at the bloc's implementation you'll notice it uses two different StreamController
s, one for the incoming events and one for the states being outputted. That allows for advanced usage like applying operators on those streams to change how the events/states are being handled(debouncing, throttling, cancelling, etc).
Another thing to add to ๐ is it is designed to be able to easily emit multiple states for a single event.
if (event is WeatherRequested) {
yield WeatherLoadInProgress();
final weather = await _getWeather();
yield WeatherLoadSuccess(weather);
}
I'm not sure what the desired outcome of this issue is. If you feel the documentation is lacking/unclear I welcome all pull requests ๐
I'm not sure what the desired outcome of this issue is.
I intended for this issue to be a clarifying discussion about how the BLoC package works in the background. Maybe that would result in some later improvements to the docs as a side effect. At any rate, if anyone has the same questions in the future, they will hopefully find this issue.
Now it is clearer to me why you chose it to be the way it is. It seems like it would not have been possible to achieve more complicated features with only, say, 2 StreamController
s and a Sink
. Being able to emit more than one state per event might be all that was necessary to counterargue my original thought of having the mapEventToState
return a state and not a Stream
of states — though I bet there's more to it.
I still think that some of what we discussed here could improve the docs — like having UML-like diagrams and mentioning the benefits of having mapEventToState
be a Stream
— and I'll see what I can do to create a pull request if you're interested.
I don't think I have much more to add, so, if you wish, we can close this issue.
Will keep this open to track the documentation request ๐
I'm not that good at reactive programming, so this question might be a bit basic. But maybe it can help improving the docs if others share the same doubts.
As far as I understand, the
mapEventToState
method returns aStream
which transforms an event internally to emit a state. However, if a new event enters the BLoC, due to this functional stream creation pattern, a newStream
would be created/returned and, I guess, a new subscription to this newStream
would need to be created. Am I right in this interpretation so far?Anyway, my real question is: why can't this be accomplished via a combination of
StreamController
(s) and the.add()
method? ThemapEventToState
would then not return aStream
of states but only states, and then be called internally in theadd
method for theStreamController
or theSink
of the BLoC states itself. If this is possible, I think it would greatly simplify the developer's life since you wouldn't need to worry aboutyield*
and whatever asynchronous complications may arise.I know I'm probably 99% wrong, but I would bet only 1% of new developers to this library won't have these questions ever, so maybe this could be included in the architecture or streams sections of the documentation.