I am facing a strange issue of some phone numbers do not receive OTP without receiving any errors, just nothing happens. Almost all the users are receiving the OTP just fine, except for only very few. How can I debug the issue? Does Firebase Auth have some log with any errors? Is my code missing something?
Here is the code (in Flutter) for sending the OTP
sendOtp(String number, BuildContext context) async {
print("Sending OTp");
state = ViewState.Busy;
await auth.verifyPhoneNumber(
phoneNumber: '+966$number',
timeout: Duration(seconds: 30),
verificationCompleted: (AuthCredential credential) {
print("Verified");
signInWCredential(credential, context, number);
},
verificationFailed: (FirebaseAuthException e) {
print("verification error code is: ${e.code}");
print("verification error message is: ${e.message}");
if (e.code == "too-many-requests") {
Dialogs.dialog(
context,
"${Localization.of(context).error}!",
Localization.of(context).tooManyOtpRequestsError,
Localization.of(context).ok,
isDoublePop: true)
.then((value) {
if (value == ConfirmAction.ACCEPT) {
Navigator.of(context).pop();
}
});
} else {
FirebaseCrashlytics.instance.recordError(e, StackTrace.current);
print("Error is: $e");
Dialogs.dialog(
context,
"${Localization.of(context).error}!",
Localization.of(context).errorSendingOtp,
Localization.of(context).ok,
isDoublePop: true);
}
},
codeSent: (String verificationId, [int resendToken]) {
print("Code sent");
print("verification ID is: $verificationId");
setVerificationId(verificationId);
print("Resend token is: $resendToken");
state = ViewState.Idle;
},
codeAutoRetrievalTimeout: (String verificationId) {},
);
}
I have contacted GCP support to investigate this issue. They reported to me that the SMS delivery in my customers' region has 85%-90% success rate. They are working with SMS providers to solve that issue.
I made a new feature request to return an error message if the SMS delivery fails. Meanwhile, I solved this issue by creating my own custom authentication API and sending SMS through another service provider.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
TextEditingController _controller = TextEditingController();
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This is a demo alert dialog.'),
TextField(
controller: _controller,
)
],
),
),
actions: <Widget>[
TextButton(
child: Text('Approve'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
void sendOTP() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: "your number",
verificationCompleted: (PhoneAuthCredential credential) async {
print("verificationCompleted");
await FirebaseAuth.instance.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
print("FirebaseAuthException");
print(e.code);
},
codeSent: (String verificationID, int token) async {
print("codeSent");
await _showMyDialog();
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.credential(
verificationId: verificationID, smsCode: _controller.text);
await FirebaseAuth.instance.signInWithCredential(phoneAuthCredential);
print("completed");
},
codeAutoRetrievalTimeout: (String verificationID) {
print("$verificationID");
},
timeout: const Duration(seconds: 60),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
child: Text(
"send otp"),
onPressed: sendOTP,
),
),
);
}
}
this is my Code for Phone Authentication using firebase and it is working fine for many mobile numbers
I faced this issue and found out from searching the internet that Firebase's Phone Authentication does not work for users who ported their number from one network to another network. I also found out that other phone authentication SDKS (other than Firebase) has the same issue. The only solution is to give your users the option to sign in with email and password or another sign-in method other than phone authentication. Offering both options will guarantee that user can sign in.
Related
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'm trying to add a feature on my app that allows users to verify their phone number by sending a code to their phone numbers. I found that I can do that by using firebase. This is how I send verification code:
Future<void> verifyPhone() async {
void verificationCompleted(AuthCredential phoneAuthCredential) {
print('verificationCompleted $phoneAuthCredential');
_phoneAuthCredential = phoneAuthCredential;
_db.updateUserField(userUid, {
'isNumberConfirmed': true,
'linkedAccounts': FieldValue.arrayUnion(['P-$phoneAuthCredential'])
});
currentUser.isNumberConfirmed = true;
currentUser.linkedAccounts.add('P-$phoneAuthCredential');
worked = true;
}
void verificationFailed(FirebaseAuthException error) {
worked = false;
}
void codeSent(String verificationId, [int code]) {
_code = code;
_verificationId = verificationId;
print('Code: $code\t_verificationId: $_verificationId');
}
void codeAutoRetrievalTimeout(String verificationId) {
worked = false;
}
_auth.verifyPhoneNumber(
phoneNumber: '+1${currentUser.phone}',
timeout: Duration(milliseconds: 60000),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
}
but whenever this method get called, I get the following messages and no verification code is sent:
I/BiChannelGoogleApi(25924): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzao#bafccce
W/DynamiteModule(25924): Local module descriptor class for com.google.firebase.auth not found.
I/FirebaseAuth(25924): [FirebaseAuth:] Preparing to create service connection to gms implementation
I'm not sure why it is not sending the code. Am I missing something?
I find your code hard to debug so I am sharing this :
First make sure you have enabled phone sign in method in firebase console ,
and added Firebase Authentication plugin and Firebase core plugin in pubspec.yaml
define your verify phone number button like this :
onPressed: (){
//code for sign in
final mobile = _phoneTextBoxController.text.trim();
registerUser(mobile, context);
}
Now define whole verifying process like this :
Future registerUser(String mobile, BuildContext context) async{
FirebaseAuth _auth = FirebaseAuth.instance;
_auth.verifyPhoneNumber(
phoneNumber: mobile,
timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential authCredential){
_auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
verificationFailed: (AuthException authException){
print(authException.message);
},
codeSent:(String verificationId, [int forceResendingToken]){
//show dialog to take input from the user
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text("Enter SMS Code"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _codeController,
),
],
),
actions: <Widget>[
FlatButton(
child: Text("Done"),
textColor: Colors.white,
color: Colors.redAccent,
onPressed: () {
FirebaseAuth auth = FirebaseAuth.instance;
smsCode = _codeController.text.trim();
_credential = PhoneAuthProvider.getCredential(verificationId: verificationId, smsCode: smsCode);
auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
)
],
)
);
},
codeAutoRetrievalTimeout: (String verificationId){
verificationId = verificationId;
print(verificationId);
print("Timout");
}
);
So what above code does ? First it sends the OTP code to user and try to sign in using this code :
verificationCompleted: (AuthCredential authCredential){
_auth.signInWithCredential(_credential).then((AuthResult result){
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => HomeScreen(result.user)
));
}).catchError((e){
print(e);
});
},
and pushes user to the home screen , you can change above code accordingly to match your app home screen . If auto retrieval fails then the codeSent : shows user a showDialog to manually enter the code . For more information visit this medium article and for complete code visit this github repository
I've managed to send an OTP using Firebase. The number also shows up under Authentication -> Users in Firebase Console. What I need to do is that if the number was verified, I want to navigate to next page. How do I do that?
One more thing is that when I enter the number and tap the button 'Verify', it just sends me an OTP message. The app doesn't prompt for the second TextFormField - "Enter OTP". Why is it doing that?
main.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Authenticate().handleAuth(),
);
}
}
authenticate.dart
class Authenticate {
//to handle auth
handleAuth() {
return StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return DashBoard();
} else {
return SignIn();
}
},
);
}
//for signin
signIn(AuthCredential authCreds) {
FirebaseAuth.instance.signInWithCredential(authCreds);
}
//for manual otp entry
signInViaOTP(smsCode, verId) {
AuthCredential authCredential = PhoneAuthProvider.getCredential(
verificationId: verId, smsCode: smsCode);
signIn(authCredential);
}
signInViaOTPT(smsCode, verId) {
AuthCredential authCredential = PhoneAuthProvider.getCredential(
verificationId: verId, smsCode: smsCode);
signIn(authCredential);
}
//for signout
signout() {
FirebaseAuth.instance.signOut();
}
}
sign_in.dart
class SignIn extends StatefulWidget {
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final formKey = new GlobalKey<FormState>();
String phoneNo, verId, smsCode;
bool codeSent = false;
Future<void> verifyNum(num) async{
final PhoneVerificationCompleted verified = (AuthCredential authRes){
Authenticate().signIn(authRes);
};
final PhoneVerificationFailed failed = (AuthException authExcep){
};
final PhoneCodeSent smsSent = (String verId, [int forceResend]){
this.verId = verId;
setState(() {
this.codeSent = true;
});
};
final PhoneCodeAutoRetrievalTimeout autoTimeOut = (String verId){
this.verId = verId;
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: num,
timeout: const Duration(seconds: 5),
verificationCompleted: verified,
verificationFailed: failed,
codeSent: smsSent,
codeAutoRetrievalTimeout: autoTimeOut
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(hintText: "Enter Phone Number"),
onChanged: (val) {
setState(() {
this.phoneNo = val;
});
},
),
),
codeSent ? Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(hintText: "Enter OTP"),
onChanged: (val) {
setState(() {
this.smsCode = val;
});
},
),
) : Container(),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: RaisedButton(
child: Center(
child: codeSent ? Text("Login") : Text("Verify"),
),
onPressed: (){
codeSent? Authenticate().signInViaOTP(smsCode, verId) : verifyNum(phoneNo);
},
),
)
],
),
),
);
}
}
What I need to do is that if the number was verified, I want to navigate to next page. How do I do that?
You'd need to listen for a successful user authentication, then let the user proceed to the next page.
handlePhoneAuth(phoneNumber).then((User user) {
if(user != null) {
NavigateToNextPage();
} else {
// Display errors
}
});
when I enter the number and tap the button 'Verify', it just sends me an OTP message. The app doesn't prompt for the second TextFormField - "Enter OTP". Why is it doing that?
You need to build your own UI for handling the OTP sent to the user's phone number. Related to the above code snippet, here's an example on how you can handle phone authentication using firebase_auth.
Future<User> handlePhoneAuth(String userPhoneNumber) async {
var auth = FirebaseAuth.instance;
auth.verifyPhoneNumber(
phoneNumber: userPhoneNumber,
codeSent: (String verificationId, int resendToken) async {
// Update the UI - wait for the user to enter the SMS code
String smsCode = 'xxxx';
// Create a PhoneAuthCredential with the code
PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: smsCode);
// Sign the user in (or link) with the credential
var userCredential = await auth.signInWithCredential(credential);
return userCredential.user;
},
verificationFailed: (FirebaseAuthException error) {
debugPrint('Phone Auth failed $error');
return null;
},
verificationCompleted: (PhoneAuthCredential phoneAuthCredential) {
// only available for Android
},
codeAutoRetrievalTimeout: (String verificationId) {
return null;
},
);
}
I am trying to integrate authentication on Flutter using firebase_auth.
However, whenever I call the verifyPhoneNumber("+256XXXXXXXXXX") I get the error message A network error (such as timeout, interrupted connection or unreachable host) has occurred., that is from the PhoneVerificationFailed callback. An for that reason cannot get the SMS.
I have tried;
Adding network permissions as seen below to my file (my internet connection works, as I am able to Google via the emulator)
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
Checking the validity of my API keys
I am totally confused as to why Flutter is unable to communicate with firebase. I have two questions.
How can I eliminate this error?
What other cases might cause this error besides a lacking internet connection?
My implimentatioin is as below;
import 'package:firebase_auth/firebase_auth.dart';
FirebaseAuth auth = FirebaseAuth.instance;
var message;
// fire this when Phone verification is completed
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) {
auth.signInWithCredential(phoneAuthCredential);
message = 'Received phone auth credential: $phoneAuthCredential';
print(message);
};
// fire this when Phone verification fails
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
message =
'Phone verification failed. Code: ${authException.code}. Message: ${authException.message}';
print(message);
};
// fire this when SMS code is sent is sent.
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
verificationId = verificationId;
print('Sent verification code');
};
// fire this when smsCode expires
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
verificationId = verificationId;
print('Auto retrival time-out');
};
// verify phone number
verifyPhoneNumber(String phoneNumber) {
auth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 30),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
print('Verification Initiated');
}
// sign in with phone.
signInWithPhoneNumber(String smsCode, String verificationId) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
final FirebaseUser user = (await auth.signInWithCredential(credential)).user;
final FirebaseUser currentUser = await auth.currentUser();
assert(user.uid == currentUser.uid);
if (user != null) {
message = 'Successfully signed in, uid: ' + user.uid;
} else {
message = 'Sign in failed';
}
}
In my case I solved allowing Outgoing Connections on the Xcode's Runner.xcworkspace
In my case, my VPN was causing the problem. Disabling the VPN and testing it again solved the error. I hope it helps.
if you are trying this out in a simulator, then this error may pop up as the internet of your simulator is not connected. To solve this you can run your app on a physical device and it will work!
I am late to the party but I found another solution.
If using an android emulator, disabling the Android Studio HTTP proxy settings can restore the network connection.
Normally when the emulator is doing a lot of work on the thread, it tends to misbehave, such a losing internet connectivity even if your PC is well connected.
My suggestion(Which worked for me) is you kill the emulator, and go to Android Studio AVD Manager and Wipe Data for that Emulator, then restart the Emulator, it worked for me.
Kindly find below the image screenshot
"I am late to the party but I found another solution.
If using an android emulator, disabling the Android Studio HTTP proxy settings can restore the network connection." - MaximeBeasse
This network error has stopped me too and I have been seeking an answer ... After reading MaximeBeasse's response above and trying it out, I found that it worked : ) Authentication now works! Finally I can move on!
Many thanks #MaximeBeasse -- salute his answer please
Open Terminal in the project folder, run the following two commands:
adb kill-server
adb start-server
If adb is not recognized, add C:\Users\USERNAME\AppData\Local\Android\Sdk\platform-tools to environement variables.
import 'package:badam/varify.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'HomePage.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FireBase Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new LoginPage(),
routes: <String, WidgetBuilder>{
'/loginpage' : (BuildContext context) => Dash(),
'/landpage' : (BuildContext context) => LoginPage(),
}
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String phoneNo, smsId, verificationId;
Future<void> verifyPhone() async{
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId){
this.verificationId = verId;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]){
this.verificationId = verId;
smsCodeDialoge(context).then((value){
print('Signed In');
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth){
print('verified');
};
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('Enter OTP'),
content: TextField(
onChanged: (value) {
this.smsId = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
new FlatButton(
onPressed: (){
FirebaseAuth.instance.currentUser().then((user){
if(user != null){
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Dash()),
);
}
else{
Navigator.of(context).pop();
signIn(smsId);
}
}
);
},
child: Text('Done', 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.of(context).pushReplacementNamed('/loginpage');
}).catchError((e){
print(e);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sign In')
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Phone Auth',style: TextStyle(fontSize: 20,color: Colors.blue),),
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
decoration: InputDecoration(
hintText: 'Enter your phone number',
),
onChanged: (value){
this.phoneNo = value;
},
),
),
SizedBox(height: 10.0),
RaisedButton(
onPressed: verifyPhone,
child: Text('Verify', style: TextStyle(color: Colors.white),),
elevation: 7.0,
color: Colors.blue,
)
],
),
);
}
}
It worked for me, hope this helps you!
I have implemented the Firebase email and Google Signin correctly and it works fine on both iOS and Android.
Then I've tried to implement the Over the Phone Auth and first it doesn't work on iPhone but then I did some research and it seems I had to upload some APNs and finally I set it up correctly and now it works on iPhone, but when I try to send SMS to Android it doesn't receive it but sends me the message that indicates the code was sent just like on iPhone.
The strangest thing is if I put the iPhone's mobile number it gets the SMS code. So I wonder if I have to enable some type of Push notifications on Android like I did on iPhone or if I have to set up some real developer account, cause right now I don't have the Google's one or there's is something else I'm missing.
Can anyone help me with this, please?
The auth method I'm using is the one of the Flutter example:
class _PhoneSignInSection extends StatefulWidget {
_PhoneSignInSection(this._scaffold);
final ScaffoldState _scaffold;
#override
State<StatefulWidget> createState() => _PhoneSignInSectionState();
}
class _PhoneSignInSectionState extends State<_PhoneSignInSection> {
final TextEditingController _phoneNumberController = TextEditingController();
final TextEditingController _smsController = TextEditingController();
String _message = '';
String _verificationId;
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: const Text('Test sign in with phone number'),
padding: const EdgeInsets.all(16),
alignment: Alignment.center,
),
TextFormField(
controller: _phoneNumberController,
decoration:
InputDecoration(labelText: 'Phone number (+x xxx-xxx-xxxx)'),
validator: (String value) {
if (value.isEmpty) {
return 'Phone number (+x xxx-xxx-xxxx)';
}
return null;
},
),
Container(
padding: const EdgeInsets.symmetric(vertical: 16.0),
alignment: Alignment.center,
child: RaisedButton(
onPressed: () async {
_verifyPhoneNumber();
},
child: const Text('Verify phone number'),
),
),
TextField(
controller: _smsController,
decoration: InputDecoration(labelText: 'Verification code'),
),
Container(
padding: const EdgeInsets.symmetric(vertical: 16.0),
alignment: Alignment.center,
child: RaisedButton(
onPressed: () async {
_signInWithPhoneNumber();
},
child: const Text('Sign in with phone number'),
),
),
Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
_message,
style: TextStyle(color: Colors.red),
),
)
],
);
}
// Example code of how to verify phone number
void _verifyPhoneNumber() async {
setState(() {
_message = '';
});
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential phoneAuthCredential) {
_auth.signInWithCredential(phoneAuthCredential);
setState(() {
_message = 'Received phone auth credential: $phoneAuthCredential';
});
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
setState(() {
_message =
'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}';
});
};
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
widget._scaffold.showSnackBar(SnackBar(
content:
const Text('Please check your phone for the verification code.'),
));
_verificationId = verificationId;
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
await _auth.verifyPhoneNumber(
phoneNumber: _phoneNumberController.text,
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
}
// Example code of how to sign in with phone.
void _signInWithPhoneNumber() async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _smsController.text,
);
final FirebaseUser user =
(await _auth.signInWithCredential(credential));
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
setState(() {
if (user != null) {
_message = 'Successfully signed in, uid: ' + user.uid;
} else {
_message = 'Sign in failed';
}
});
}
}
And my App level build.gradle have this dependencies:
dependencies {
implementation 'com.google.firebase:firebase-auth:19.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
As the guide says:
Add the dependency for the Firebase Authentication Android library to
your module (app-level) Gradle file (usually app/build.gradle):
implementation 'com.google.firebase:firebase-auth:19.0.0'
And of course I have apply plugin: 'com.google.gms.google-services' in the same build.gradle
EDIT: When it test with whitelisted phone numbers, I'm successfully signing in on Android.
Phone number in iPhone is receiving SMS successfully from both Android and iOS Apps.
Phone number in Android is not receiving in any SMS from either Android or iOS App.
So the problem is not really with the App but with Phone Number/SIM in Android phone. This can happen if the Phone is in roadming or if there are any restrictions no the Phone numbers, then Firebase might not send SMS. Apparantly this is what happening. To make sure this is the problem, better to test with another phone Number. If its working with the new phone number, we can conclude its problem with a particular Phone number but not with your phone.
You might want to re-write following code (in those lines):
_auth.signInWithCredential(phoneAuthCredential);
setState(() {
_message = 'Received phone auth credential: $phoneAuthCredential';
});
to
_auth.signInWithCredential(phoneAuthCredential)
.then((user) {setState(){
_message = 'Received phone auth credential: $phoneAuthCredential';
}})
.catchError((error) {
_message = 'Something went wrong: $error';
});
So that you are sure making sure _auth.signInWithCredential is successful before informing user.