2

I am trying to verify phone number using firebase. But getting unexpected result from async function. Here is my code :

  bool isVerificationSuccess = false;

  Future<bool> verifyUserPhoneNumber(String phoneNumber) async {
    await FirebaseAuth.instance.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      timeout: Duration(seconds: 60),
      verificationCompleted: (credential) => verificationComplete(credential),
      verificationFailed: (authException) => verificationFailed(authException),
      codeAutoRetrievalTimeout: (verificationId) =>
          codeAutoRetrievalTimeout(verificationId),
      codeSent: (verificationId, [code]) => smsCodeSent(verificationId, [code]),
    );
    print("Status from service : $isVerificationSuccess");
    return isVerificationSuccess;
  }

  verificationComplete(AuthCredential credential) async {
    FirebaseUser user = await FirebaseAuth.instance.currentUser();

    user.linkWithCredential(credential).then((_) {
      print("User Successfully Linked");
      isVerificationSuccess = true;
    }).catchError((e) {
      print("Linking Error : ${e.toString()}");
    });
  }

Here is the output :

 Status from service : false
 User Successfully Linked

So here verifyUserPhoneNumber function returns even before the completion of FirebaseAuth.instance.verifyPhoneNumber(), so its not returning expected data (always false) even when the verification is successful. Whats wrong here?

3 Answers 3

1

The issue is that the function call in this line:

verificationCompleted: (credential) => verificationComplete(credential),

is not awaited, even though the function itself is asynchronous. So what happens is:

  1. verifyUserPhoneNumber is called from somewhere
  2. FirebaseAuth.instance.verifyPhoneNumber is awaited
  3. Within this call, verificationComplete(credential) is called and, because it itself contains an await statement, it will be queued up by the Dart event loop. So whatever happens after FirebaseUser user = await FirebaseAuth.instance.currentUser(); is delayed!
  4. verifyUserPhoneNumber now continues executing and returns false.
  5. After a value has already been returned, it is now verificationComplete's turn to process. It will change the boolean value, and then exit.

My recommendation here is not to use a global variable, but instead, for example, call verificationComplete after FirebaseAuth.instance.verifyPhoneNumber has already fully finished processing and returned.

Edit: This is how you could theoretically fix the issue, though it is not particularly pretty I do admit:

  Future<bool> verifyUserPhoneNumber(String phoneNumber) async {
    final completer = Completer<AuthCredential>();
    await FirebaseAuth.instance.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      timeout: Duration(seconds: 60),
      verificationCompleted: completer.complete,
      verificationFailed: completer.completeError,
      codeAutoRetrievalTimeout: (verificationId) =>
          codeAutoRetrievalTimeout(verificationId),
      codeSent: (verificationId, [code]) => smsCodeSent(verificationId, [code]),
    );
    try {
      final credential = await completer.future;
      return await verificationComplete(credential);
    } catch (e) {
      verificationFailed(e);
      return false;
    }
  }

  Future<bool> verificationComplete(AuthCredential credential) async {
    FirebaseUser user = await FirebaseAuth.instance.currentUser();
    try {
      await user.linkWithCredential(credential);
      print("User Successfully Linked");
      return true;
    } catch (e) {
      print("Linking Error : ${e.toString()}");
      return false;
    }
  }
Sign up to request clarification or add additional context in comments.

5 Comments

Calling verificationComplete requires credential. So how can I get credential after the completion of FirebaseAuth.instance.verifyPhoneNumber. The whole thing is really confusing!
I added an example of what you could do.
Tried it. But no change.
Okay I may have misunderstood the situation. The documentation isn't entirely clear about this, but FirebaseAuth.instance.verifyPhoneNumber might be exiting before calling any of the callbacks. Please try my new example.
In vain, your solutions not even do the main task (number verification).
1

if u can place in isVerificationSuccess = true; inside function verificationComplete(credential) then u might get true value

5 Comments

Yeah, though its already in verificationComplete() function, may be the value need to be changed outside of then() block. +1 for that.
But why verifyUserPhoneNumber() function returns value without waiting? It returns value before verificationComplete() completes.
i think it has something to do with streams : If successful, it also signs the user in into the app and updates the [onAuthStateChanged] stream.
if u can somehow implement onAuthStateChanged in your code then it can work
Ok, I'll try that.
1

So here verifyUserPhoneNumber function returns even before the completion of FirebaseAuth.instance.verifyPhoneNumber(), so its not returning expected data (always false) even when the verification is successful. Whats wrong here?

FirebaseAuth.instance.verifyPhoneNumber uses a method channel plugins.flutter.io/firebase_auth and calls a native method for authentication.

It uses the call back specified in the argument verificationCompleted once verification completes. However, from the function definitions, it seems that FirebaseAuth.instance.verifyPhoneNumber expects a type void Function(AuthCredential). However, you provide Future<void> Function(AuthCredential)

Whats wrong here?

It seems that what is wrong is to expect that FirebaseAuth.instance.verifyPhoneNumber will wait for an async call back Future<void> Function(AuthCredential) to complete when it expects a void Function (AuthCredential).

So it just calls your verificationComplete(AuthCredential credential) async implementation which returns a Future<void>. At this moment, FirebaseAuth.instance.verifyPhoneNumber is complete and the control moves ahead to print and return isVerificationSuccess which is still false as the future returned by verificationComplete(AuthCredential credential) async is not resolved yet.

So,

  1. Return a Future<void> from verifyUserPhoneNumber
  2. Update the App's state using a setState inside verificationComplete after you assign isVerificationSuccess = true; to rebuild your app after a successful verification

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.