I am trying to conduct a phone number login by sending sms code to my phone device using firebase auth. But the await keyword does not seem to work here. Why? my assumption is when I assign await to a function, Flutter prints the null value of the verificationId variable, verificationId gets its value within the _sendCodeToPhoneNumber function. Hence this could mean the function _sendCodeToPhoneNumber hasn't finished yet but the its next line of code (print(verificationId) in this case) is already executed. But if I am mistaken, then what is the problem? and more important, please teach me how to resolve problem? Because I have to get verificationId's value and pass it to the next class.
String verificationId;
Future<void> _sendCodeToPhoneNumber() async {
...
final PhoneCodeSent codeSent = (String verificationId, [int forceResendingToken]) async {
this.verificationId = verificationId; //this.verificationId here is not null because it has got a value
print('Code sent to ${_phoneNumberController.text} with $verificationId');
};
...
await FirebaseAuth.instance.verifyPhoneNumber(
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
phoneNumber: _phoneNumberController.text,
codeSent: codeSent,
timeout: const Duration(seconds: 120),
verificationCompleted: verificationCompleted,
verificationFailed: vericationFailed
);
}
...
FlatButton(onPressed: (){
await _sendCodeToPhoneNumber();
print(verificationId); // printed null despite having assigned `await` to the _sendCodeToPhoneNumber function
...
})
Change the function to the following and return the verificationId:
Future<String> _sendCodeToPhoneNumber() async {
final PhoneCodeSent codeSent = (String verificationId, [int forceResendingToken]) async {
this.verificationId = verificationId; //this.verificationId here is not null because it has got a value
print('Code sent to ${_phoneNumberController.text} with $verificationId');
};
await FirebaseAuth.instance.verifyPhoneNumber(
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
phoneNumber: _phoneNumberController.text,
codeSent: codeSent,
timeout: const Duration(seconds: 120),
verificationCompleted: verificationCompleted,
verificationFailed: vericationFailed
);
return this.verificationId;
}
Also change the onPressed, when using await you need to use async:
FlatButton(onPressed: (()async{
var result = await _sendCodeToPhoneNumber();
print(result);
}))
final PhoneCodeSent codeSent = (String verificationId, [int forceResendingToken]) async {
this.verificationId = verificationId; //this.verificationId here is not null because it has got a value
print('Code sent to ${_phoneNumberController.text} with $verificationId');
};
i think you dont need async here
Related
This is the function I am using for to generate OTP and submitting OTP.
The OTP came once, but after that the OTP is not coming.
I even tried 2 different country code and numbers.
class AuthService with ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
//Sign In
String userphone = "";
String verificationId;
String errorMessage = "";
Future verifyPhone(String phonenumber) async {
final PhoneVerificationFailed verificationFailed =
(FirebaseAuthException authException) {
print("Auth Exception is ${authException.message}");
};
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
print("verification id is $verificationId");
this.verificationId = verificationId;
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
this.verificationId = verificationId;
};
await _auth.verifyPhoneNumber(
phoneNumber: "+7" + phonenumber,
verificationCompleted: null,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout,
timeout: const Duration(seconds: 60),
);
}
Future signIn({#required String smsCode, context}) async {
AuthCredential authCredential = PhoneAuthProvider.credential(
verificationId: verificationId,
smsCode: smsCode,
);
UserCredential result = await _auth.signInWithCredential(authCredential);
User user = result.user;
await checkUser(result);
return _userFromFirebase(user);
}
}
}
The custom Button I am using to trigger the generate otp.
I tried on emulator as well as on my Real Device.
CustomButton(
buttonName: "Generate OTP",
function: () async {
if (_formKeySignIn.currentState.validate() &&
phonenumber.length == 10) {
Provider.of<AuthService>(context, listen: false).userphone =
phonenumber;
Provider.of<AuthService>(context, listen: false)
.verifyPhone(phonenumber);
widget.changeView();
} else {
setState(() {
error = "Please enter a valid phone number";
});
}
},
),
The code is correct! The mistake I was doing was, I also have to add the SHA Codes from the App Signing Section from the Play Publisher to the Firebase along with your apps SHA code. Then download the Google services json file again and replace your apps json file and generate the app bundle again.
I am using Firebase_auth package for verifying phone number, Firebase send code but when I check the validity of code it just show error. when I print code sent it is different with the code that I receive. any one know please help me.
thanks in advance.
here is my code:
await FirebaseAuth.instance.verifyPhoneNumber(
timeout: Duration(seconds: 60),
phoneNumber: phoneNumber,
verificationCompleted:
(PhoneAuthCredential phoneAuthCredential) {
print(phoneAuthCredential.smsCode);
print("complete");
},
verificationFailed: (e) {
print("failed");
print(e);
},
codeSent: (same, a) {
print(a);
print(same);
Navigator.of(context).pushNamed(
VerifyScreen.routName,
arguments: {
'code': a,
'customerId': customerId,
},
);
},
codeAutoRetrievalTimeout: (sa) {
print(sa);
print("timeout");
},
);
I know the code received cannot be printed, but it comes in encrypted form verificationId.
When the entered code is verified, you send the code that was entered by the user and the verificationId to Firebase to verify it.
If your goal is to be verified automatically without entering the code through this verificationCompleted, you can transfer the user automatically.
Note: Provided that the sim card is on the same phone to which the code was sent.
Additional information: If you want the process to be automatic, use the following code:
Future singInPhoneNumber() async {
FirebaseAuth _auth = FirebaseAuth.instance;
_auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential phoneAuthCredential) async {
//Automatically verify
Navigator.of(context).pushNamed(
VerifyScreen.routName,
arguments: {
'code': a,
'customerId': customerId,
},
);
debugPrint("Firebase Auth :Auto Verification Completed");
},
verificationFailed: (FirebaseAuthException error) {
debugPrint("ERROR Firebase Auth : AuthException : ${error.message}");
showToast(context, 'Something went wrong, please try again later');
},
codeSent: (String verificationId, [int forceResendingToken]) {
setState(() {
this.verificationId = verificationId;
});
},
codeAutoRetrievalTimeout: (String verificationId) {
return null;
},
);
}
If you only want to enter the code manually:
AuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: codeController.text);
_auth
.signInWithCredential(credential)
.then((value) => {
print('The operation completed successfully')
})
.catchError((onError) {
print(onError.toString());
});
I have implemented the Firebase phone authentication on my flutter application. But anytime I send invoke the initAuth method, I get code sent on my console then after 60 secs timed out. That I understand is simply coming from my code but I expected an SMS or an auto login. It seems I don't ever get the verificationCompleted method invoked.
Below is my code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class PhoneVerification{
initAuth({BuildContext context, String phoneNumber}) async{
print(phoneNumber);
var firebaseAuth = FirebaseAuth.instance;
await firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: Duration(seconds: 60),
codeSent:(String verificationId, [int forceResendingToken]){
print("Code Sent");
},
codeAutoRetrievalTimeout: (String verificationId){
print("Timed out");
},
verificationCompleted: (AuthCredential auth) async {
print("This User is authenticated by google play services");
firebaseAuth.signInWithCredential(auth)
.then((AuthResult result) =>{
if(result != null && result.user != null){
print("Authentication is Successful")
}else{
print("Authentication failed!")
}
})
.catchError((error){
print(error);
});
},
verificationFailed: (AuthException authException){
print('Error message: ' + authException.message);
},
);
}
}
This is the method I wrote to invoke the initAuth method:
Future<void> sendSMS({context}) async{
User user = Provider.of<UserProvider>(context, listen: false).user;
PhoneVerification phoneVerification = PhoneVerification();
await phoneVerification.initAuth(context: context, phoneNumber: user.phone);
}
Then finally, I am making the actual call onPress of a button in a component. Below is how I invoked the sendSMS method.
EnterPhone(onPress: ()=> sendSMS(context: context)),
for me :
// For firebase auth
final auth = FirebaseAuth.instance;
//
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) async {
final res = await auth.signInWithCredential(phoneAuthCredential);
// Todo After Verification Complete
);
};
//
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
print('Auth Exception is ${authException.message}');
};
//
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
print('verification id is $verificationId');
verId = verificationId;
};
//
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
verId = verificationId;
};
//
await auth.verifyPhoneNumber(
// mobile no. with country code
phoneNumber: '+91${_mobile.text}',
timeout: const Duration(seconds: 30),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
I'm trying to set up phone authentication on my app but I got stuck with this error "A value of type '(FirebaseUser) → Null' can't be assigned to a variable of type '(AuthCredential) → void'.". Any Idea on how to fix it? Thanks in advance for your help!
class _AuthScreenState extends State<AuthScreen> {
String phoneNo;
String smsCode;
String verificationId;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve =(String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int
forceCodeResend]){
this.verificationId = verId;
};
final PhoneVerificationCompleted verifiedSuccess = (FirebaseUser
user) {
print("Verificado");
};
final PhoneVerificationFailed verifiedFailed = (AuthException
exception) {
print("${exception.message}");
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: this.phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifiedFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve);
}
The type PhoneVerificationCompleted used to receive FirebaseUser and return void, but this changed in some of the latest updates. Now it receives AuthCredential. Just change the type in the method that you assigned to the variable verifiedSuccess.
It should look like this:
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential credential) {
print("Verificado");
};
I am looking for a way to resend the OTP in firebase verifyPhoneNumber in flutter.
I have gone through the example about phoneAuth but could not find a way to resend OTP.
There is an option of forceResendingToken
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
this.verificationId = verificationId;
_smsCodeController.text = testSmsCode;
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: this._phone,
codeAutoRetrievalTimeout: autoRetrieval,
codeSent: smsCodeSent,
forceResendingToken: ,//how to get this token
timeout: const Duration(seconds: 40),
verificationCompleted: verifSuccessful,
verificationFailed: verifFailed);
}
How to use this token to resend the OTP.
From the Firebase reference documentation:
the ForceResendingToken obtained from onCodeSent(String, PhoneAuthProvider.ForceResendingToken) callback to force re-sending another verification SMS before the auto-retrieval timeout.
So in your case that'd be the PhoneCodeSent callback of the verifyPhoneNumber() call.
You have to record the value of resendToken from the codesent callback and pass it to the forceresendingtoken. Also, the inbuilt timeout duration is 30 seconds So make sure you tap on resend button after timeout seconds, you may use timerbutton package it's very handy in this case.
Firebase sends 3x same SMS code and then change it to different.
String _verificationId = "";
int? _resendToken;
Future<bool> sendOTP({required String phone}) async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phone,
verificationCompleted: (PhoneAuthCredential credential) {},
verificationFailed: (FirebaseAuthException e) {},
codeSent: (String verificationId, int? resendToken) async {
_verificationId = verificationId;
_resendToken = resendToken;
},
timeout: const Duration(seconds: 25),
forceResendingToken: _resendToken,
codeAutoRetrievalTimeout: (String verificationId) {
verificationId = _verificationId;
},
);
debugPrint("_verificationId: $_verificationId");
return true;
}