Stuck returning verificationId from Future<String> method Flutter

258

Finally managed to use it properly from Bloc.

Methods are:

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;
      });
  }

Callbacks are defined in bloc Streams:

@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);
      }
  }

Hope this helps others. Cheers.

Share:
258
Vincenzo
Author by

Vincenzo

Updated on December 25, 2022

Comments

  • Vincenzo
    Vincenzo over 1 year

    I'm trying to authenticate user with phone and as I'm using flutter_bloc and Repository the approach I'm having is:

    1. Send event with phone number to Bloc(OK).
    2. Call verifyPhoneNumber passing in the phone number (OK).
    3. Return the verificationId received from either codeAutoRetrievalTimeout or codeSent callbacks.
    4. Send it to UI from BlocListener with a State.
    5. Update UI to enter the verification code.
    6. Send smsCode and verificationId to another method to link phone AuthCredential to user.

    My method returns before callbacks are called as verifyPhoneNumber completes first so it's returning a null String.

    What I'm doing wrong? As always many thanks for your help.

    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
      }