Parado de retornar verificationId do método Future <String> Flutter

Oct 23 2020

Estou tentando autenticar o usuário com telefone e, como estou usando flutter_bloc e Repositório, a abordagem que estou adotando é:

  1. Enviar evento com número de telefone para o Bloco (OK).
  2. Ligue verifyPhoneNumberpassando o número do telefone (OK).
  3. Retorne o verificationIdrecebido de um codeAutoRetrievalTimeoutou de codeSentretornos de chamada.
  4. Envie para a interface do usuário BlocListenercom um estado.
  5. Atualize a IU para inserir o código de verificação.
  6. Envie smsCode e verificationId para outro método para vincular o telefone AuthCredential ao usuário.

Meu método retorna antes que os retornos de chamada sejam chamados, conforme verifyPhoneNumber é concluído primeiro, portanto, ele está retornando uma String nula.

O que estou fazendo de errado? Como sempre, muito obrigado pela sua ajuda.

Future<String> verifyPhoneNumber(String phoneNumber) async {
    print('verifyPhoneNumber() started');
    String verifyId;

    await _firebaseAuth
        .verifyPhoneNumber(
            phoneNumber: phoneNumber,
            timeout: Duration(minutes: 0),
            //error: Undefined class 'PhoneAuthCredential'.
//      verificationCompleted: (PhoneAuthCredential credential) async {
            verificationCompleted: (AuthCredential credential) {
            },
            //error: Undefined class 'FirebaseAuthException'.
//      verificationFailed: (FirebaseAuthException e) {
            verificationFailed: (AuthException e) {
              if (e.code == 'invalid-phone-number') {
                print(
                    'verifyPhoneNumber() -> verificationFailed -> The provided phone number is not valid.');
              } else {
                print(
                    'verifyPhoneNumber() -> verificationFailed :${e.message}'); } // Handle other errors }, codeAutoRetrievalTimeout: (String verificationId) { // Auto-resolution timed out... // verifyId = verificationId; print( 'verifyPhoneNumber() -> codeAutoRetrievalTimeout callback called'); }, //error: The argument type 'Null Function(String, int)' can't be assigned to the parameter type 'void Function(String, [int])'. // codeSent: (String verificationId, int resendToken) { codeSent: (String verificationId, [int resendToken]) { verifyId = verificationId; print( 'verifyPhoneNumber() -> codeSent callback called : $verifyId'); // correct
              return verifyId;
            }
            )
        .then((value) {
      print('verifyId is $verifyId'); // null // return verifyId; }).whenComplete(() => print('Complete')); print('verifyPhoneNumber() ->returning verifyId: $verifyId');
    return verifyId; // without it method doesn't return, with it returns null
  }

Respostas

Vincenzo Oct 25 2020 at 03:13

Finalmente consegui usá-lo corretamente do Bloc.

Os métodos são:

UserRepository

Future<void> verifyPhone(
      {@required String phoneNumber,
      @required Duration timeOut,
      @required PhoneVerificationFailed phoneVerificationFailed,
      @required PhoneVerificationCompleted phoneVerificationCompleted,
      @required PhoneCodeSent phoneCodeSent,
      @required PhoneCodeAutoRetrievalTimeout autoRetrievalTimeout}) async {
    _firebaseAuth.verifyPhoneNumber(
        phoneNumber: phoneNumber,
        timeout: timeOut,
        verificationCompleted: phoneVerificationCompleted,
        verificationFailed: phoneVerificationFailed,
        codeSent: phoneCodeSent,
        codeAutoRetrievalTimeout: autoRetrievalTimeout);
  }

  Future<AuthResult> verifyAndLinkAuthCredentials(
  {@required String verificationId, @required String smsCode}) async {
    AuthCredential authCredential = PhoneAuthProvider.getCredential(
        verificationId: verificationId, smsCode: smsCode);

//    return _firebaseAuth.signInWithCredential(authCredential);

    FirebaseUser user = await _firebaseAuth.currentUser();
    return user.linkWithCredential(authCredential).catchError((e) {
      print('UserRepository.verifyAndLinkAuthCredentials() error: $e');
//      return;
      });
  }

Retornos de chamada são definidos em fluxos de bloco:

@override
  Stream<AuthenticationState> mapEventToState(
      AuthenticationEvent event) async* {
    // phone verification
    if (event is VerifyPhoneNumberEvent) {
      print('VerifyPhoneNumberEvent received');
      yield VerifyingState();
      yield* _mapVerifyPhoneNumberToState(event);
    } 
    if (event is PhoneCodeSentEvent) {
      print('PhoneCodeSentEvent received');
      yield OtpSentState();
    }
    if (event is VerificationCompletedEvent) {
      print('VerificationCompletedEvent received');
      yield VerificationCompleteState(firebaseUser: event.firebaseUser, isVerified: event.isVerified);
    }
    if (event is VerificationExceptionEvent) {
      print('VerificationExceptionEvent received');
      yield VerificationExceptionState(message: event.message);
    }

    if ( event is PhoneCodeAutoRetrievalTimeoutEvent){
      yield PhoneCodeAutoRetrievalTimeoutState(verificationId: event.verificationId);
    }

    if(event is SendVerificationCodeEvent) {
      yield VerifyingState();
      yield*_mapVerificationCodeToState(event);
    }


  }
  
  Stream<AuthenticationState> _mapVerifyPhoneNumberToState(VerifyPhoneNumberEvent event) async* {
    print('_mapVerifyPhoneNumberToState V2 started');
    final phoneVerificationCompleted = (AuthCredential authCredential) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationCompleted');
      _userRepository.getCurrentUser().catchError((onError) {
        print(onError);
      }).then((user) {
        add(VerificationCompletedEvent(firebaseUser: user, isVerified:  true));
      });
    };
    final phoneVerificationFailed = (AuthException authException) {
      print('_mapVerifyPhoneNumberToState PhoneVerificationFailed');
      print(authException.message);
      add(VerificationExceptionEvent(onError.toString()));
    };
    final phoneCodeSent = (String verificationId, [int forceResent]) {
      print('_mapVerifyPhoneNumberToState PhoneCodeSent');
      this.verificationId = verificationId;
      add(PhoneCodeSentEvent());
    };
    final phoneCodeAutoRetrievalTimeout = (String verificationId) {
      print('_mapVerifyPhoneNumberToState PhoneCodeAutoRetrievalTimeout');
      this.verificationId = verificationId;
      add(PhoneCodeAutoRetrievalTimeoutEvent(verificationId: verificationId));
    };

    await _userRepository.verifyPhone(
        phoneNumber: event.phoneNumber,
        timeOut: Duration(seconds: 0),
        phoneVerificationFailed: phoneVerificationFailed,
        phoneVerificationCompleted: phoneVerificationCompleted,
        phoneCodeSent: phoneCodeSent,
        autoRetrievalTimeout: phoneCodeAutoRetrievalTimeout);
  }


  Stream<AuthenticationState> _mapVerificationCodeToState(SendVerificationCodeEvent event) async* {
    print('_mapVerificationCodeToState started');
      AuthResult result = await _userRepository.verifyAndLinkAuthCredentials(verificationId: verificationId, smsCode: event.smsCode)
          .catchError((e){
            print('verifyAndLinkAuthCredentials error: $e');
      });
      print(result);

      if (result != null) {
        yield VerificationCompleteState(firebaseUser: result.user, isVerified: true);
      } else {
        yield OtpExceptionState(message: "Invalid otp!", verificationId: verificationId);
      }
  }

Espero que isso ajude outras pessoas. Felicidades.