i'm would like to add a button for existing user to verify phone number on their profile page,
Future<void> phoneVerification(String _mobile) async {
await _auth.verifyPhoneNumber(
phoneNumber: _mobile,
verificationCompleted: (PhoneAuthCredential phoneAuthCredential) async {
await userInfo.linkWithCredential(phoneAuthCredential);
},
verificationFailed: (FirebaseAuthException e) async {
// Handle error
print(e);
},
codeSent: (String verificationId, int resendToken) async {
},
codeAutoRetrievalTimeout: (String verificationId) async {
},
);
}
however nothing happened even i entered the OTP, verificationCompleted is not called
FlatButton(
onPressed: () async {
setState(() {
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.credential(verificationId: verificationId, smsCode: OTP);
});
},
child: Text("VERIFY"),
color: Colors.blue,
textColor: Colors.white,
),
Related
im having trouble with authentication on Firebase.
there is a login screen that asks for the number, then on the next button press it goes to the OTP screen that results in authenticating the user.
But after the verification number is typed, it doesnt authenticate and says Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
this is the code that i use to verify the user
final String PhoneNumber; //globally declared
_verifyPhone() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: widget.PhoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async {
await FirebaseAuth.instance
.signInWithCredential(credential)
.then((value) async {
if (value.user != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
EmailScreen(PhoneNumber: widget.PhoneNumber),
),
);
}
});
},
verificationFailed: (FirebaseAuthException e) {
print(e.message);
},
codeSent: (String verificationID, int resendToken) {
setState(() {
_verificationCode = verificationID;
});
},
codeAutoRetrievalTimeout: (String verificationID) {
setState(() {
_verificationCode = verificationID;
});
},
timeout: Duration(seconds: 60));
}
and this is the OnSubmit function of the PinEntryTextField
PinEntryTextField(
fields: 6,
showFieldAsBox: true,
onSubmit: (String pin) async {
try {
await FirebaseAuth.instance
.signInWithCredential(PhoneAuthProvider.credential(
verificationId: _verificationCode, smsCode: pin))
.then((value) async {
if (value.user != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EmailScreen(
PhoneNumber: widget.PhoneNumber,
)));
}
});
} catch (e) {
FocusScope.of(context).unfocus();
print(e);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Failed because $e"),
));
}
},
),
Now it works sometimes, but sometimes it doesn't, that's the weird thing.
any help would be appreciated..
thanks
Never mind, i had to wait a few seconds until the SMS code "arrives"..
Because i was using a test phone number
Silly me 😂😂
Please verify if the phone number is not going to be null. In my case phone number is going null so this error was come.
class AuthProvider with ChangeNotifier {
FirebaseAuth _auth = FirebaseAuth.instance;
String smsOtp;
String verificationId;
String error ='';
UserServices _userServices = UserServices();
bool loading = false;
LocationProvider locationData = LocationProvider();
String screen;
double latitude;
double longitude;
String address;
Future<void> verifyPhone({BuildContext context, String number}) async {
this.loading=true;
notifyListeners();
final PhoneVerificationCompleted verificationCompleted =
(PhoneAuthCredential credential) async {
this.loading=false;
notifyListeners();
await _auth.signInWithCredential(credential);
};
final PhoneVerificationFailed verificationFailed =
(FirebaseAuthException e) {
this.loading=false;
print(e.code);
this.error=e.toString();
notifyListeners();
};
final PhoneCodeSent smsOtpSend = (String verId, int resendToken) async {
this.verificationId = verId;
};
//open dialog to enter received OTP sms
smsOtpDialog(context, number);
try {
_auth.verifyPhoneNumber(
phoneNumber: number,
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: smsOtpSend,
codeAutoRetrievalTimeout: (String verId){
this.verificationId = verId;
},
);
} catch (e) {
this.error=e.toString();
this.loading=false;
notifyListeners();
print(e);
}
}
Future<bool> smsOtpDialog(BuildContext context, String number) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Column(
children: [
Text('Verification Code'),
SizedBox(height: 6,),
Text('Enter 6 digits OTP received via SMS',
style: TextStyle(color: Colors.grey,fontSize: 12),
),
],
),
content: Container(
height: 85,
child: TextField(
textAlign: TextAlign.center,
keyboardType: TextInputType.number,
maxLength: 6,
onChanged: (value){
this.smsOtp = value;
},
),
),
actions: [
FlatButton(
onPressed: ()async{
try{
PhoneAuthCredential phoneAuthCredential =
PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: smsOtp);
final User user = (await _auth.signInWithCredential(phoneAuthCredential)).user;
if(user!=null){
this.loading=false;
notifyListeners();
_userServices.getUserById(user.uid).then((snapShot){
if(snapShot.exists){
//user data already exists
if(this.screen=='Login'){
//need to check user data already exists in database or not
//if its login,no new data, so no need to update
Navigator.pushReplacementNamed(context, HomeScreen.id);
}else{
//need to update new selected address
print('${locationData.latitude} : ${locationData.longitude}');
updateUser(id: user.uid, number: user.phoneNumber);
Navigator.pushReplacementNamed(context, HomeScreen.id);
}
}else{
//user data doesn't exists
//will create new data in db
_createUser(id: user.uid, number: user.phoneNumber);
Navigator.pushReplacementNamed(context, HomeScreen.id);
}
});
}else
{
print('Login Failed');
}
} catch(e){
this.error = 'Invalid OTP';
notifyListeners();
print(e.toString());
Navigator.of(context).pop();
}
},
child: Text('SUBMIT',style: TextStyle(color: Theme.of(context).primaryColor),),
),
],
);
}).whenComplete(() {
this.loading=false;
notifyListeners();
});
}
void _createUser({String id, String number} ) {
_userServices.createUserData({
'id':id,
'number':number,
'latitude':this.latitude,
'longitude':this.longitude,
'address':this.address
});
this.loading=false;
notifyListeners();
}
Future <bool> updateUser({String id, String number,} ) async{
try{
_userServices.updateUserData({
'id':id,
'number':number,
'latitude':this.latitude,
'longitude':this.longitude,
'address':this.address
});
this.loading=false;
notifyListeners();
return true;
}catch(e){
print('Error $e');
return false;
}
}
}
======== Exception caught by gesture ===============================================================
The following NoSuchMethodError was thrown while handling a gesture:
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 _MapScreenState.build. (package:flutter_hasho_user/screens/map_screen.dart:146:44)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
#3 _InkResponseState.build. (package:flutter/src/material/ink_well.dart:1111:38)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#3d7f8
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(184.2, 647.6)
finalLocalPosition: Offset(164.2, 15.6)
button: 1
sent tap down
====================================================================================================
I am pretty sure your issue is calling .uid on a user in your else block when the user is null, which throws an error. You should make sure that the user class is initialized or something similar atleast.
else{
_createUser(id: user.uid, number: user.phoneNumber); // [user] is null
Navigator.pushReplacementNamed(context, HomeScreen.id);
}
I have just developed an app which requires phone authentication. Inside login screen I can able to achieve to login via phone. But my concern is : for the first time when I enter phone number and enter verification number it comes back to login which in reality expected to navigate to homescreen. For the second try system is able to work and navigate to home screen as expected. Here is my code block. I am wondering which part of the code I make mistake since login info pop back again and system is able to navigate to home screen after second try:
My code block :
class _LoginScreenState extends State<LoginScreen> {
String phoneNo, smssent, verificationId;
get verifiedSuccess => null;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
smsCodeDialoge(context).then((value) {
print("Doğrulama Kodu Gönderildi");
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth) {};
final PhoneVerificationFailed verifyFailed = (AuthException e) {
print('${e.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialoge(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('DoÄŸrulama Kodunu Giriniz'),
content: TextField(
onChanged: (value) {
this.smssent = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
FlatButton(
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
} else {
Navigator.of(context).pop();
signIn(smssent);
}
});
},
child: Text(
'DoÄŸrulama Yap',
style: TextStyle(color: Colors.blue),
),
),
],
);
});
}
Future<void> signIn(String smsCode) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential).then((user)
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
);
}).catchError((e) {
print(e);
});
}
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomeScreen()),
);
Here you are calling pop, which will take you to the previous screen if the user is not null, and after that, you are pushing the HomeScreen into the stack. Try not poping and just pushing, by the way, are you using routes and streamprovider in your code?
I want to allow users to register an account on my app using flutter with email/password, and then send code to the phone. So when user enters their data to Sign up, it should register them with their email and then use their phone to the verity code. Also the user can't complete the registration and go to the Home page Without checking the entered code.
But in my code it's not working what i want do it.
My click function:
dynamic ruselt= await _auth.regsiterwithemail(_emailcontroller.text,_passwordcontroller.text);
if (ruselt!=null){
setState(() async {
loading =false;
verfitycode().verfityphoen(context);
});
}else{
setState(() {
loading=false;
SweetAlert.show(context,
title: "Title",
style: SweetAlertStyle.confirm,
subtitle: "Subtitle");
});
}
My registration function:
Future regsiterwithemail(String email , String password )
async {
try{
AuthResult result =await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user =result.user;
print('oky');
return _userfirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
class for verity code
class verfitycode {
String phonenumber;
String smsCode;
String vialdid;
GlobalKey<FormState>_form;
Future<void> verfityphoen(BuildContext context)async{
final PhoneCodeAutoRetrievalTimeout AutoRetriv =(String verid) {
this.vialdid =verid;
};
final PhoneCodeSent smsCodeset =(String verid,[int forceResendingToken]){
this.vialdid=verid;
smscodeDialog(context);
};
final PhoneVerificationCompleted verfiedcompletd=(AuthCredential user){
print('verfild');
};
final PhoneVerificationFailed verfilederror =(AuthException exception){
print('${exception.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber:"+967776523152",
timeout: const Duration(seconds:5),
verificationCompleted: verfiedcompletd,
verificationFailed: verfilederror,
codeSent: smsCodeset,
codeAutoRetrievalTimeout: AutoRetriv
);
}
Future<bool> smscodeDialog(BuildContext context){
Alert(
context: context,
title: "رمز التØقق",
content: Column(
children: <Widget>[
Form(
key: _form,
child: Directionality(
textDirection: TextDirection.rtl,
child:
TextField(
onChanged: (val){
this.smsCode=val;
},
decoration:InputDecoration (
icon: Icon(Icons.supervisor_account),
labelText: 'ادخل رمز التØقق',
),
),
)
)
],
),
buttons: [
DialogButton(
child: Text('خروج', style: TextStyle(color: Colors.white, fontSize:18)), onPressed:()=>Navigator.pop(context)),
DialogButton(
onPressed: (){
// _form.currentState.validate();
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.pop(context);
Navigator.push(context,
MaterialPageRoute(builder: (context) => Home()));
} else {
Navigator.pop(context);
_testSignlink();
}
});
},
child: Text(
"ادخل الرمز",
style: TextStyle(color: Colors.white, fontSize:18),
),
),
]).show();
}
_testSignlink() async {
FirebaseUser user;
String _smsCodeController;
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: vialdid,
smsCode: smsCode,
);
await user.linkWithCredential(credential).then((user){
print(user.user.uid);
}).catchError((onError){
print(onError.toString());
});
_smsCodeController = '';
return 'signInWithPhoneNumber succeeded: $user';
}
}
I am implementing phoneNumberAuth signup. But there's a problem
When i clicked authbottom, terminated iOS app
code :
String smsCode, verificationId;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) {
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
print('asd');
smsCodeDialog(context).then((value) {
print('Signed in');
});
};
final PhoneVerificationCompleted verificationCompleted = (AuthCredential credential) {
print('verified');
};
final PhoneVerificationFailed verfiFailed = (AuthException exception) {
print('${exception.message}+ddddddddddd');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: this.phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verfiFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('SMS 코드를 ìž…ë ¥í•´ì£¼ì„¸ìš”'),
content: TextField(
onChanged: (value) {
this.smsCode = value;
},
),
contentPadding: EdgeInsets.all(10),
actions: <Widget>[
FlatButton(
child: Text('Done'),
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/');
} else {
Navigator.of(context).pop();
SIGNIn();
}
});
},
)
],
);
});
}
SIGNIn() async{
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
print('진행중');
FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
setState(() {
if(user != null){
print('success!');
}else{
print('sign in failed');
}
});
}
error code :
*** First throw call stack:
(
0 CoreFoundation 0x00000001169bb8db __exceptionPreprocess + 331
1 libobjc.A.dylib 0x0000000115f66ac5 objc_exception_throw + 48
2 CoreFoundation 0x00000001169d9c94 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x00000001169c0623 ___forwarding___ + 1443
4 CoreFoundation 0x00000001169c2418 _CF_forwarding_prep_0 + 120
5 Runner 0x000000010e97d966 -[FIRPhoneAuthProvider internalVerifyPhoneNumber:UIDelegate:completion:] + 118
6 Runner 0x000000010e97ccc0 __64-[FIRPhoneAuthProvider verifyPhoneNumber:UIDelegate:completion:]_block_invoke + 272
7 libdispatch.dylib 0x0000000115e16ccf _dispatch_call_block_an<…>
Lost connection to device.
How can i do fix my code ?
Until someone helped me, i trying doing fix
It's hard because there aren't many explain errors.
I'd appreciate it if someone would help me.
Somebody Help me :(
pop removes the top level widget. Not sure its a good idea to put logic after that. Better you re-arrange your code like
// Only gets SMS, no functionality
Future<String> getSmsCode(BuildContext context) {
return showDialog<String>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('SMS 코드를 ìž…ë ¥í•´ì£¼ì„¸ìš”'),
content: TextField(
onChanged: (value) {
this.smsCode = value;
},
),
contentPadding: EdgeInsets.all(10),
actions: <Widget>[
FlatButton(
child: Text('Done'),
onPressed: () {
Navigator.of(context).pop(this.smsCode);
},
)
],
);
},
);
}
SIGNIn() async {
String smsCode = await getSmsCode(context);
if (smsCode != null && !smsCode.isNotEmpty) {
print('User cancelled SMS dialog');
return;
}
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
print('진행중');
FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
setState(() {
if (user != null) {
print('success!');
} else {
print('sign in failed');
}
});
}
Now only invoke SIGNIn, it will first get SMS code then signin using that SMS code. Hope it helps.