0
votes

A design pattern type question. I'm working with the TextToSpeech package for Flutter.

The user presses play (UI) this passes play event to Bloc, the bloc creates a TTSPlayer object and passes it a list of strings (TTSScript). The TTS reads aloud each string in the list by iterating through.

The PROBLEM: I want the bloc to dispatch an event every time the TTSPlayer moves to the next string in the list, so that i can display the current string on screen.

BUT: How do I pass a 'currentTextUpdatedEvent' from the TTSPlayer to the bloc, which I can then pass back to the UI.

I TRIED: I was hoping I could do something like:

BlocProvider.of<TTSPlayerBloc>(context).add(updateCurrentTtsString(currentTtsString));

from inside the TTSPlayer class... but of course, this class is not a widget it has no context. Should I use a callback? What would work here...? Should I move the TTSPlayer out of the bloc and into the screen instead?

  Stream<TTSPlayerState> mapEventToState(TTSPlayerEvent event) async* {
    TTSPlayer lessonPlayer = new TTSPlayer();
  
    if (event is TTSPlayerScreenLoadedEvent) {
      //get the script
      TTSScript myScript = await scriptRepository.fetchScript(event.script);
      // play it
      lessonPlayer.play(myLessonScript);
      //// How can the lessonplayer inform this Stream every time it's current string has changed? 
      yield TTSPlayingState();
    }

    /// something like this??? But how to send this event from TTSPlayer class... 
    if (event is updateCurrentTtsString){
    yield NewStringState(newstring)
    }

UPDATE So, i added a callback inside the TTSPlayer. So the bloc now looks like this:

      Stream<TTSPlayerState> mapEventToState(TTSPlayerEvent event) async* {
        TTSPlayer lessonPlayer = new TTSPlayer();
   ///
    final newStringCallback = () {
      this.add(updateCurrentTtsString(lessonPlayer.currentTtsString));
    };
   ///
        if (event is TTSPlayerScreenLoadedEvent) {
          //get the script
          TTSScript myScript = await scriptRepository.fetchScript(event.script);
          // play it
          lessonPlayer.play(myLessonScript, newStringCallback););
          yield TTSPlayingState();
        }


    if (event is updateCurrentTtsString) {
      print('New String');
      yield TTSPlayingState(event.currentString);
    }

The updateCurrentStringEvent does fire every time IF if remove the yield statement... the moment I include the yield statement, it stops the TTSPlayer from continuing. I don't get why the player would stop, but it does.

1

1 Answers

0
votes

SOLUTION:

Ok, so after some fiddling, I set up a new stream inside the bloc like this:

  final currentStringStreamController = StreamController<String>();
  StreamSink<String> get currentStringSink => currentStringStreamController.sink;
  Stream<String> get currentStringStream => currentStringStreamController.stream;

Then, in the callback that I set up in the original post I changed:

 this.add(updateCurrentTtsString(lessonPlayer.currentTtsString));

to:

  currentStringSink.add(string);

Then just used darts streambuilder from the async package to build a textbox widget.

Hope it helps someone.