Search code examples
flutterbloc

Emitting states from a bloc using bloc.emit


I'm building a phone authentication ui (OTP) using firebase_auth library with BLoC pattern (a bit of an overkill for this ui, but the whole project is in BLoC, so). I'm processing the authentication within the BLoC, as such:


  @override
  Stream<PhoneAuthState> mapEventToState(PhoneAuthEvent event) async* {
    ....
    else if(event is PhoneNumberSubmittedEvent) yield* _handlePhoneNumberSubmittedEvent(event);
    ....
  }

  Stream<PhoneAuthState> _handlePhoneNumberSubmittedEvent(PhoneNumberSubmittedEvent event) async*{
      yield SendingCodeState();

      await Firebase.initializeApp();
      var firebaseAuth = FirebaseAuth.instance;

      await firebaseAuth.verifyPhoneNumber(
        phoneNumber: _buildPhoneNumber(),
        timeout: Duration(seconds: 0),
        verificationCompleted: (firebaseUser) {},
        codeAutoRetrievalTimeout: (String verificationId) {},


        // Relevant code

        codeSent: (id, [forceResendingToken]) =>
          emit(_codeSentState(id, forceResendingToken)),

        verificationFailed: (error) =>
          emit(_verificationFailedState(error)),
      );
  }

Because the results of my _codeSentState and _verificationFailedState functions cannot be yielded from within the handlePhoneNumberSubmittedEvent method, I used emit (which is actually working fine).

However, as I was looking through BLoC docs, I found that emit is @protected, and the docs stating:

[emit] should never be used outside of tests.

So I have three questions:

  1. Why shouldn't emit be used outside of tests?
  2. Is there an alternative to emit? (other than yielding in response to events in mapEventToState)
  3. Is there a way to yield the result of a function that is passed as a parameter to a method/constructor call? (in my case, is there a way to yield the results of _codeSentState and/or _verificationFailedState that are called within firebaseAuth.verifyPhoneNumber.codeSent and firebaseAuth.verifyPhoneNumber.verificationFailed respectively?)

Solution

  • In flutter_bloc version 8.0.0, this issue was resolved. The method mapEventToState was replaced with the more efficient on<Event> to respond to events. on method provides an emitter as a parameter to its callback, which can be used to emit states as needed. In other words, the code I mentioned in OP can now be written as follows

    // constructor
    MyBloc() : super(MyInitialState) {
      on<PhoneNumberSubmittedEvent>((event, emit) async {
          emit(SendingCodeState());
    
          await Firebase.initializeApp();
          var firebaseAuth = FirebaseAuth.instance;
    
          await firebaseAuth.verifyPhoneNumber(
            phoneNumber: _buildPhoneNumber(),
            timeout: Duration(seconds: 0),
            verificationCompleted: (firebaseUser) {},
            codeAutoRetrievalTimeout: (String verificationId) {},
    
            codeSent: (id, [forceResendingToken]) =>
              emit(_codeSentState(id, forceResendingToken)),
    
            verificationFailed: (error) =>
              emit(_verificationFailedState(error)),
          );
      });
    }