3

Is there any elegant way to map incoming streams from a private api directly inside mapEventToState() without having to create redundant private events in the bloc?

I came with this solution. It's ok with one single stream, but with multiple streams it starts to get a mess. Thanks in advance.

// (don't mind the imports, this is the bloc file)

class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {


  final MyPrivateApi api = MyPrivateApi.instance; // singleton

  ExampleBloc() {
    // api has a stream of booleans
    api.myStream.listen((b) {
    // if it's true fire this event
    if (b) this.add(_MyPrivateEvent());
  }

  @override
  ExampleState get initialState => InitialExampleState();

  @override
  Stream<ExampleState> mapEventToState(
    ExampleEvent event,
  ) async* {
    if (event is _MyPrivateEvent) {
      yield SomeState;
    }
  }



// private Event
class _MyPrivateEvent extends ExampleEvent {

}

Paulo Bruckmann
  • 331
  • 4
  • 7

3 Answers3

0

As I can see, you can subscribe on event updates in your screen, and push event from screen to Bloc if need some calculations. Code will be more clean.

Yauhen Sampir
  • 1,989
  • 15
  • 16
  • The problem is that I can't subscribe to a stream inside mapEventToState() , forcing me to have to have private events to then yield the states – Paulo Bruckmann Nov 26 '19 at 17:05
0

Your way seems to be the only way works and seems to be used - see this bloc issue: https://github.com/felangel/bloc/issues/112 and this example project: https://github.com/algirdasmac/streams_and_blocs

Just make sure to dispose the subscription that gets returned by api.myStream.listen.

Previous answer

The following DOES NOT work for infinite streams because the generator function will await until the stream finishes. This can only be used for stream the complete fast, like maybe an upload/download progress. See accepted answers here Dart yield stream events from another stream listener and here Dart/Flutter - "yield" inside a callback function

  ExampleBloc() {
    _MyInitEvent();
  }

  @override
  Stream<ExampleState> mapEventToState(
    ExampleEvent event,
  ) async* {
    if (event is _MyInitEvent) {
      await for (bool b in api.myStream) {
        if (b) yield SomeState;
      }
    }
  }
kuhnroyal
  • 7,188
  • 1
  • 34
  • 47
  • I think it won't work because the streams are not finite and using it on a yield function will stop upcoming events until the await for stream is closed. – Paulo Bruckmann Nov 26 '19 at 17:00
  • Yes you are right, I guess there is no other way. I am thinking about using bloc in the next project but this is a huge hurdle. – kuhnroyal Nov 26 '19 at 17:12
0

Build another block that encapsulate your stream of bytes.

You can make two events (ByteRead and ByteConsume) and two states (ByteWaiting and ByteAvailable).

Byteread and ByteAvailable should have a _byte field for storing data. Your bytebloc has a subscriber listening the stream and every time it reads a byte it fires a ByteRead event.

You should also add to the bloc a consume() method that gives the last byte readed and fires the ByteConsume event.

The two states and events are mutual:

  • you start in bytewaiting and every time you have a ByteRead you change to ByteAvailable and
  • every time you have a ByteConsume you change back to ByteWaiting.
Peyman Mohamadpour
  • 17,954
  • 24
  • 89
  • 100
user3042236
  • 279
  • 3
  • 5