How to Use Switch Statement To Handle FirebaseAuthException - firebase

I have a Flutter app that allows users to register/login to their accounts using FirebaseAuth. Signing in requires users to enter their email address and password. I am trying to use a switch statement to handle for when the user is unable to sign in due to entering the wrong email address, wrong password, etc.
login_screen.dart below contains code for the Log In button and when pressed, makes a call to FirebaseAuthHandler (from firebase_auth_handler.dart) when there is a FirebaseAuthException.
login_screen.dart
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final user = await _auth.signInWithEmailAndPassword(
email: email, password: password);
if (user != null) {
Navigator.pushNamed(context, LandingScreen.id);
}
setState(() {
showSpinner = false;
});
} catch (errorCode) {
FirebaseAuthHandler(errorCode).handleErrorCodes();
setState(() {
showSpinner = false;
});
}
},
),
firebase_auth_handler.dart
class FirebaseAuthHandler {
FirebaseAuthHandler(this.errorCode);
FirebaseAuthException errorCode;
handleErrorCodes() {
switch (errorCode) {
case "[firebase_auth/wrong-password] The password is invalid or the user does not have a password.":
print("Invalid password.");
break;
case "[firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.":
print("Invalid email address.");
break;
}
}
}
The problem is that I get an error for switch(errorCode) that says,
Type 'FirebaseAuthException' of the switch expression isn't assignable to the type 'String' of case expressions
The two case statements I am using are what's printed to my Console when I print the exceptions. How can I provide a case of type FirebaseAuthException that will work for my switch statement?

I was able to figure this one out. I changed switch(errorCode) to switch(errorCode.code) and changed the cases to what I have below. I was able to find a list of error codes for signInWithEmailAndPassword here and plugged some of the error codes in for the case expressions.
As #rickimaru commented, you could also use errorCode.message instead of errorCode.code if you like, as long as you find the corresponding message and plug it in for the case expressions.
class FirebaseAuthHandler {
handleErrorCodes(FirebaseAuthException errorCode) {
switch (errorCode.code) {
case "wrong-password":
print("Invalid password!!!");
break;
case "user-not-found":
print("Invalid email address!!!");
break;
}
}
}

Related

Flutter Firebase: Error when user tries to register with an already existing email

I'm currently testing my app, when I try to register a new account with an email already it in use, I get the following error:
ArgumentError (Invalid argument(s) (onError): The error handler of Future.catchError must return a value of the future's type)
When I click sign in the app still informs the user that this email is already in use but the error crashes the app. If I try to test any of the other errors, my app does not crash.
I'm not quite sure how to begin to resolve this due to not really being sure why is error is occurring.
*.dart
void signUp(String email, String password) async {
if (GlobalKey<FormState>().currentState!.validate()) {
try {
await FirebaseAuth.instance //<--- Error stops here
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) => {postDetailsToFirestore()})
.catchError((e) {
Fluttertoast.showToast(msg: e!.message);
});
} on FirebaseAuthException catch (error) {
switch (error.code) {
case "invalid-email":
errorMessage = "Your email address appears to be incorrect.";
break;
case "wrong-password":
errorMessage = "Your password is wrong.";
break;
case "user-not-found":
errorMessage = "User with this email doesn't exist.";
break;
case "user-disabled":
errorMessage = "User with this email has been disabled.";
break;
case "too-many-requests":
errorMessage = "Too many requests";
break;
case "operation-not-allowed":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
Fluttertoast.showToast(msg: errorMessage!);
print(error.code);
}
}
}
postDetailsToFirestore() async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
UserModel userModel = UserModel();
// writing all the values
userModel.email = user!.email;
userModel.uid = user.uid;
userModel.userName = NameEditingController.text;
userModel.password = passwordEditingController.text;
await firebaseFirestore
.collection("users")
.doc(user.uid)
.set(userModel.toMap());
Fluttertoast.showToast(msg: "Account created successfully ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
async/await is just a syntactic sugar to handle promises. It makes your code more readable, and combining it with try/catch you can have a more clear way of handling exceptions.
I also recommend to handle the situation if the mobile device does not have Internet access, and a SocketException is thrown. For this kind of exception you need import 'dart:io';
Don't forget to add a simple catch as well, because you can have exceptions other than FirebaseAuthException and SocketException, and these will be not caught if you use only on.(You don't need the catch after on.)
Try it this way:
try {
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: email,
password: password);
await postDetailsToFirestore();
} on FirebaseAuthException (error) {
...
} on SocketException {
...
} catch (e) {
..
}

Display error message outside build widget

I am using a Model class to authenticate user before registering or logging.the problem is that i don't know a way to print error message to the user in snackbar,because no widget is defined in this class.
How can i display error message to user from Model Class?
Model class:
class FireAuth {
static Future<User> registerUsingEmailPassword({
String name,
String email,
String password,
}) async {
FirebaseAuth auth = FirebaseAuth.instance;
User user;
try {
UserCredential userCredential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
user = userCredential.user;
await user.updateDisplayName(name);
await user.reload();
user = auth.currentUser;
//check if email is registered before
//add user data to firestore
CollectionReference users = FirebaseFirestore.instance.collection('users');
users.doc(user.uid).set({
'uid':user.uid,
'img_url':'0',
'name': name,
'phone': '',
'email': email,
'job_title':'',
'university':'',
'procedures':'',
'expert_in':'',
})
.then((value) => print("User Added"))
.catchError(
(error) => print("Failed to add user: $error"));
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
return user;
}
}
I need 'The account already exists for that email.' error message to display to user,not only printing it in log.
Excellent question, and I'll try to answer in the general so as to benefit your overall pattern in handling this very important case.
Depending on BuildContext is a common inconvenience in flutter. And it often comes up, but for good reason. You can think of it like this: You need the context because you need to specify where in the tree that UI is going to show. Knowing how to handle these cases makes the difference between beginner and more advanced flutter developers.
So one way is to pass the BuildContext around, but I wouldn't recommended it.
Lets say I have a function foo that returns some Future Rather than change the signature of the function to accept context, you can simply await the function and use the context in the callback already in your UI. For example,
instead of
Future foo(BuildContext context) {
try {
// await some async process
// Use context to show success.
} catch (e) {
// Use context to show failure.
}
}
You can do this
GestureDetector(
onTap: () async {
try {
await foo();
// Use context to show success.
} catch (e) {
// Use context to show failure.
}
},
child: // some child
),
The point is in the second example the context is already there in the widget. The signuture of foo is simpler. It requires some restructuring. Here I'm assuming that the series of events is traced back to a GestureDetector but it could be anything else.

Firebase Authentication with Flutter not working

I am trying to create a signup page which should give an error message if user with particular email id already exist. But it's not working.
signUp() {
if (formkey.currentState!.validate()) {
Map<String, String> userDataMap = {
"name": usernameC.text,
"email": emailC.text
};
setState(() {
isLoading = true;
});
authMethods.signUp(emailC.text, passwordC.text).then((value) {
databaseMethods.uploadUserData(userDataMap);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => ChatRoom()));
});
}
}
It calls the signUp() function from auth.dart given below
UserData? _userFromFirebase(User? user) {
return user != null ? UserData(userid: user.uid) : null;
}
Future signUp(String email, String pass) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: pass);
User? user = result.user;
return _userFromFirebase(user);
} catch (e) {
print(e}
}
Every time I signup with same email it doesn't give any error.
If you sign up with the same email you should get this message:
[firebase_auth/email-already-in-use] The email address is already in use by another account.
I use print(e.hashCode) and then use this hash code to show an error message.
Ok I tried this method and it worked out. Just added null check for the "value" attribute in.
authMethods.signUp(emailC.text, passwordC.text).then((value)
It was returning null without any other message. That's why I was unable to see the error.

Platform Exception in Flutter - How to handle [duplicate]

Please does anyone know how to catch firebase Auth exceptions on flutter and display them?
Note: I am not interested in the console (catcherror((e) print(e))
I need something that is more effective, e.g " user doesn't exist" So that I can then pass it to a string and display it.
Been dealing with this for months.
Thanks in advance.
I have tried replacing print(e) with // errorMessage=e.toString(); and then passing it to a function, all efforts have been futile.
FirebaseAuth.instance
.signInWithEmailAndPassword(email: emailController.text, password: passwordController.text)
.then((FirebaseUser user) {
_isInAsyncCall=false;
Navigator.of(context).pushReplacementNamed("/TheNextPage");
}).catchError((e) {
// errorMessage=e.toString();
print(e);
_showDialog(errorMessage);
//exceptionNotice();
//print(e);
I want to be able to extract the exception message and pass the exception message to a dialog that I can then display to the user.
NEW ANSWER (18/09/2020)
If you are using firebase_auth: ^0.18.0, error codes have changed!
For instance: ERROR_USER_NOT_FOUND is now user-not-found
I could not find any documentation about that, so I went into the source code and read comments for every error codes. (firebase_auth.dart)
I don't use all error codes in my app (e.g verification, password reset...) but you will find the most common ones in this code snippet:
(It handles old and new error codes)
String getMessageFromErrorCode() {
switch (this.errorCode) {
case "ERROR_EMAIL_ALREADY_IN_USE":
case "account-exists-with-different-credential":
case "email-already-in-use":
return "Email already used. Go to login page.";
break;
case "ERROR_WRONG_PASSWORD":
case "wrong-password":
return "Wrong email/password combination.";
break;
case "ERROR_USER_NOT_FOUND":
case "user-not-found":
return "No user found with this email.";
break;
case "ERROR_USER_DISABLED":
case "user-disabled":
return "User disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
case "operation-not-allowed":
return "Too many requests to log into this account.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
case "operation-not-allowed":
return "Server error, please try again later.";
break;
case "ERROR_INVALID_EMAIL":
case "invalid-email":
return "Email address is invalid.";
break;
default:
return "Login failed. Please try again.";
break;
}
}
If you are using firebase_auth: ^0.18.0, error codes have changed! Check the next answer.
I just coded myself a way to do this without Platform dependent Code:
This is possible since .signInWithEmailAndPassword correctly throws Errors with defined codes, that we can grab to identify the error and handle things in the way the should be handled.
The following example creates a new Future.error, if any error happens, and a Bloc is then configured to shovel that data through to the Widget.
Future<String> signIn(String email, String password) async {
FirebaseUser user;
String errorMessage;
try {
AuthResult result = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
user = result.user;
} catch (error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email address appears to be malformed.";
break;
case "ERROR_WRONG_PASSWORD":
errorMessage = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
errorMessage = "User with this email doesn't exist.";
break;
case "ERROR_USER_DISABLED":
errorMessage = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
errorMessage = "Too many requests. Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
(21/02/20) EDIT: This answer is old and the other answers contains cross platform solutions so you should look at theirs first and treat this as a fallback solution.
The firebase auth plugin doesn't really have a proper cross-platform error code system yet so you have to handle errors for android and ios independently.
I'm currently using the temporary fix from this github issue: #20223
Do note since its a temp fix, don't expect it to be fully reliable as a permanent solution.
enum authProblems { UserNotFound, PasswordNotValid, NetworkError }
try {
FirebaseUser user = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} catch (e) {
authProblems errorType;
if (Platform.isAndroid) {
switch (e.message) {
case 'There is no user record corresponding to this identifier. The user may have been deleted.':
errorType = authProblems.UserNotFound;
break;
case 'The password is invalid or the user does not have a password.':
errorType = authProblems.PasswordNotValid;
break;
case 'A network error (such as timeout, interrupted connection or unreachable host) has occurred.':
errorType = authProblems.NetworkError;
break;
// ...
default:
print('Case ${e.message} is not yet implemented');
}
} else if (Platform.isIOS) {
switch (e.code) {
case 'Error 17011':
errorType = authProblems.UserNotFound;
break;
case 'Error 17009':
errorType = authProblems.PasswordNotValid;
break;
case 'Error 17020':
errorType = authProblems.NetworkError;
break;
// ...
default:
print('Case ${e.message} is not yet implemented');
}
}
print('The error is $errorType');
}
Expanding on the accepted answer I thought it's worth to mention that:
The firebase_auth plugin has AuthException.
As pointed out in this Github issue post you can have the same error codes for Android and iOS.
If you have this code in a non-UI layer you can use rethrow or better throw your own formatted exceptions and catch those at the UI level (where you'll know exactly the kind of error you'll get).
try {
AuthResult authResult = await FirebaseAuth.instance.signInWithCredential(credential);
// Your auth logic ...
} on AuthException catch (e) {
print('''
caught firebase auth exception\n
${e.code}\n
${e.message}
''');
var message = 'Oops!'; // Default message
switch (e.code) {
case 'ERROR_WRONG_PASSWORD':
message = 'The password you entered is totally wrong!';
break;
// More custom messages ...
}
throw Exception(message); // Or extend this with a custom exception class
} catch (e) {
print('''
caught exception\n
$e
''');
rethrow;
}
the exceptions can be handled using, FirebaseAuthException class.
Here's the code for login using email and password:
void loginUser(String email, String password) async {
try {
await _auth.signInWithEmailAndPassword(email: email, password:password);
} on FirebaseAuthException catch (e) {
// Your logic for Firebase related exceptions
} catch (e) {
// your logic for other exceptions!
}
You can use your own logic to handle the error, e.g show a alert dialog, etc.
Same can be done for creating a user.
in Auth Class have this function:
Future signUpWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
FirebaseUser user = result.user;
return user;
} catch (e) {
return e;
}
}
The catch error above returns a runTimeType of PlatformException and a PlatformException in flutter has 3 properties check here!
in your Dart file, implement this on button listeners:
String error = "";
dynamic result = await _auth.signUpWithEmailAndPassword(email, password);
if (result.runtimeType == PlatformException) {
if (result.message != null) {
setState(() {
error = result.message;
});
} else {
setState(() {
error = "Unknown Error";
});
}
}
So I faced this issue today and instead of hardcoding the error messages to display, I decided to use string manipulations and I managed to get the message.
The goal was to get the message (everything after ]).
Example: get this => Password should be at least 6 characters from this => [firebase_auth/weak-password] Password should be at least 6 characters.
So using the exception from the try-catch, I converted it to string first, then replaced the first 14 characters (from '[' to '/') with nothing, so I was left with weak-password] Password should be at least 6 characters.
Then the split function with ']' pattern to search the remaining string for the ']' symbol and split the whole string into two with the index of the ']' symbol as the pivot. This returns a list with two Strings; 'weak-password' and 'Password should be at least 6 characters'. Use the index 1 to get the second string which is the error message.
e.toString().replaceRange(0, 14, '').split(']')[1]
I manage the firebase auth exception with the exceptions codes of the version
firebase_auth: ^3.3.6
firebase_core: ^1.12.0
This is the code that work for me:
Future<void> loginWithEmailAndPassword({
required String email,
required String password,
}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
} on firebase_auth.FirebaseAuthException catch (e) {
switch (e.code) {
case "invalid-email":
//Thrown if the email address is not valid.
throw InvalidEmailException();
case "user-disabled":
//Thrown if the user corresponding to the given email has been disabled.
throw UserDisabledException();
case "user-not-found":
//Thrown if there is no user corresponding to the given email.
throw UserNotFoundException();
case "wrong-password":
throw PasswordExceptions();
//Thrown if the password is invalid for the given email, or the account corresponding to the email does not have a password set.
default:
throw UncknownAuthException();
}
}
}
And I create the exceptions to control the messeges to display in the UI later like this:
class AuthenticationException implements Exception {}
class InvalidEmailException extends AuthenticationException {}
class PasswordExceptions extends AuthenticationException {}
class UserNotFoundException extends AuthenticationException {}
class UserDisabledException extends AuthenticationException {}
class UncknownAuthException extends AuthenticationException {}
Hope it help to someone having problems to handle auth exceptions!
in Dart you can react to different Exceptions using the on syntax. Since Firebase uses its own PlatformException you can easily catch them with:
try {
AuthResult result = await signUp(email, password);
} on PlatformException catch (e) {
print(e.message);
} on Exception catch (e) {
print(e);
}
PlatformException brings a code as well as a message which can be displayed in the UI, i.e. :
PlatformException(ERROR_EMAIL_ALREADY_IN_USE, The email address is already in use by another account., null)
I was stuck on this for a while too, i created this gist with all the available errors here with an example, covers all the platform exception codes
Example for handling sign up exceptions
Future<String> signUp(String email, String password, String firstName) async {
FirebaseUser user;
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
user = result.user;
name = user.displayName;
email = user.email;
Firestore.instance.collection('users').document(user.uid).setData({
"uid": user.uid,
"firstName": firstName,
"email": email,
"userImage": userImage,
});
} catch (error) {
switch (error.code) {
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Anonymous accounts are not enabled";
break;
case "ERROR_WEAK_PASSWORD":
errorMessage = "Your password is too weak";
break;
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email is invalid";
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
errorMessage = "Email is already in use on different account";
break;
case "ERROR_INVALID_CREDENTIAL":
errorMessage = "Your email is invalid";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
Example for handling sign in exceptions
Future<String> signIn(String email, String password) async {
FirebaseUser user;
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = result.user;
name = user.displayName;
email = user.email;
userId = user.uid;
} catch (error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email address appears to be malformed.";
break;
case "ERROR_WRONG_PASSWORD":
errorMessage = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
errorMessage = "User with this email doesn't exist.";
break;
case "ERROR_USER_DISABLED":
errorMessage = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
errorMessage = "Too many requests. Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
I prefer to create api layer response and error models and wrap the firebase plugin error and response objects in them. For sign in with email and password i have this
#override
Future<dynamic> loginWithEmailAndPassword(String email, String password) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return FirebaseSignInWithEmailResponse();
} catch (exception) {
return _mapLoginWithEmailError(exception);
}
}
ApiError _mapLoginWithEmailError(PlatformException error) {
final code = error.code;
if (code == 'ERROR_INVALID_EMAIL') {
return FirebaseSignInWithEmailError(
message: 'Your email is not valid. Please enter a valid email',
type: FirebaseSignInWithEmailErrorType.INVALID_EMAIL);
} else if (code == 'ERROR_WRONG_PASSWORD') {
return FirebaseSignInWithEmailError(
message: 'Your password is incorrect',
type: FirebaseSignInWithEmailErrorType.WRONG_PASSWORD);
} else if (code == 'ERROR_USER_NOT_FOUND') {
return FirebaseSignInWithEmailError(
message: 'You do not have an account. Please Sign Up to'
'proceed',
type: FirebaseSignInWithEmailErrorType.USER_NOT_FOUND);
} else if (code == 'ERROR_TOO_MANY_REQUESTS') {
return FirebaseSignInWithEmailError(
message: 'Did you forget your credentials? Reset your password',
type: FirebaseSignInWithEmailErrorType.TOO_MANY_REQUESTS);
} else if (code == 'ERROR_USER_DISABLED') {
return FirebaseSignInWithEmailError(
message: 'Your account has been disabled. Please contact support',
type: FirebaseSignInWithEmailErrorType.USER_DISABLED);
} else if (code == 'ERROR_OPERATION_NOT_ALLOWED') {
throw 'Email and Password accounts are disabled. Enable them in the '
'firebase console?';
} else {
return FirebaseSignInWithEmailError(
message: 'Make sure you have a stable connection and try again'
type: FirebaseSignInWithEmailErrorType.CONNECTIVITY);
}
}
I never return the AuthResult from firebase. Instead i listen to the onAuthStateChanged stream and react accordingly if there is a change.
I have the same error of "firebse platform exeption:" in flutter using "Firebase auth"
and it didn't resolve even using try catch and trim() method in passing arrguments.
The problem is when you run app using Button "Run" in main.dart it won't callback and catch the error.
Solution: In Vscode terminal type "Flutter run" (for debug mode). or "Flutter run --release" (for release mode) now you won't face platform exception.
I had an issue where I didn't want the "com.google.firebase.FirebaseException: An internal error has occurred. [ Unable to resolve host "www.googleapis.com":No address associated
with hostname ]" which would indicate to a user that the backend being used is firebase. Thus, I just used toString().replaceAll()
Future<void> signIn() async {
final formState = _formkey.currentState;
var _date = DateTime.now();
if (formState!.validate()) {
emailFocus!.unfocus();
passwordFocus!.unfocus();
formState.save();
setState(() {
isloading = true;
_errorMessage = '';
});
try {
UserCredential user = await _firebaseAuth.signInWithEmailAndPassword(
email: _email, password: _password!);
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', _email);
await FirebaseFirestore.instance
.collection('Users Token Data')
.doc(user.user!.uid)
.set({'Email': _email, 'Token': _token, 'Date': _date});
Navigator.pushNamedAndRemoveUntil(
context, RouteNames.homePage, (e) => false);
} on FirebaseAuthException catch (e) {
setState(() {
isloading = false;
_errorMessage = e.message.toString().replaceAll(
'com.google.firebase.FirebaseException: An internal error has' +
' occurred. [ Unable to resolve host "www.googleapis.com":' +
"No address associated with hostname ]",
"Please Check Network Connection");
});
print(e.message);
}
}
}
}
Just incase if you don't wanna reveal too much information from the error message.
I have also recently faced this error and, I have found out that the .catchError() callback wasn't being called in the debug mode (which is when you click the Run->Start Debugging button in VSCode).
However, when you type in flutter run -d , then, the .catchError() method gets called back as it is not in debug mode.
To get your preferred simulator's code paste this line of code in the terminal:
instruments -s devices
If that doesn't work, you can also try pasting this:
xcrun simctl list
The ```.catchError()`` method will get called unlike before and the code inside that will get executed as expected!
Additionally, the app won't crash anymore with a PlatformException() and instead you will get a log like this one:
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'uid' was called on null.
Receiver: null
I have been facing this problem in Google Sign In too, in which the .catchError() was not being called!
In conclusion, if you have some error with handling errors in Firebase Authentication, you should first try to first run in through the terminal. Thanks, and I hope this helps!
try this , i had the same proplem and this code worked with me
catch (e) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Wrong UserName or Password')));
}
Firebase-Auth Latest version provides a simple way to get error code and error Messages using the following try catch statements:
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: "name#example.com",
password: "SuperSecretPassword!"
);
} on FirebaseAuthException catch (e) {
print('Failed with error code: ${e.code}');
print(e.message);
}
Source:
Firebase Documentation
This code will show a red snack bar that contains the exception cause like "User already in use by other account".
auth.createUserWithEmailAndPassword(
email: email,
password: password,
).onError((error,stackTrace){
if(error.runtimeType == FirebaseAuthException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(error.toString().replaceRange(0, 14, '').split(']')[1]),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
}
try {
final newuser = await _auth.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text);
// print("Done");
} catch (e) {
print(e);
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(e.message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}

How to Handle Firebase Auth exceptions on flutter

Please does anyone know how to catch firebase Auth exceptions on flutter and display them?
Note: I am not interested in the console (catcherror((e) print(e))
I need something that is more effective, e.g " user doesn't exist" So that I can then pass it to a string and display it.
Been dealing with this for months.
Thanks in advance.
I have tried replacing print(e) with // errorMessage=e.toString(); and then passing it to a function, all efforts have been futile.
FirebaseAuth.instance
.signInWithEmailAndPassword(email: emailController.text, password: passwordController.text)
.then((FirebaseUser user) {
_isInAsyncCall=false;
Navigator.of(context).pushReplacementNamed("/TheNextPage");
}).catchError((e) {
// errorMessage=e.toString();
print(e);
_showDialog(errorMessage);
//exceptionNotice();
//print(e);
I want to be able to extract the exception message and pass the exception message to a dialog that I can then display to the user.
NEW ANSWER (18/09/2020)
If you are using firebase_auth: ^0.18.0, error codes have changed!
For instance: ERROR_USER_NOT_FOUND is now user-not-found
I could not find any documentation about that, so I went into the source code and read comments for every error codes. (firebase_auth.dart)
I don't use all error codes in my app (e.g verification, password reset...) but you will find the most common ones in this code snippet:
(It handles old and new error codes)
String getMessageFromErrorCode() {
switch (this.errorCode) {
case "ERROR_EMAIL_ALREADY_IN_USE":
case "account-exists-with-different-credential":
case "email-already-in-use":
return "Email already used. Go to login page.";
break;
case "ERROR_WRONG_PASSWORD":
case "wrong-password":
return "Wrong email/password combination.";
break;
case "ERROR_USER_NOT_FOUND":
case "user-not-found":
return "No user found with this email.";
break;
case "ERROR_USER_DISABLED":
case "user-disabled":
return "User disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
case "operation-not-allowed":
return "Too many requests to log into this account.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
case "operation-not-allowed":
return "Server error, please try again later.";
break;
case "ERROR_INVALID_EMAIL":
case "invalid-email":
return "Email address is invalid.";
break;
default:
return "Login failed. Please try again.";
break;
}
}
If you are using firebase_auth: ^0.18.0, error codes have changed! Check the next answer.
I just coded myself a way to do this without Platform dependent Code:
This is possible since .signInWithEmailAndPassword correctly throws Errors with defined codes, that we can grab to identify the error and handle things in the way the should be handled.
The following example creates a new Future.error, if any error happens, and a Bloc is then configured to shovel that data through to the Widget.
Future<String> signIn(String email, String password) async {
FirebaseUser user;
String errorMessage;
try {
AuthResult result = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
user = result.user;
} catch (error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email address appears to be malformed.";
break;
case "ERROR_WRONG_PASSWORD":
errorMessage = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
errorMessage = "User with this email doesn't exist.";
break;
case "ERROR_USER_DISABLED":
errorMessage = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
errorMessage = "Too many requests. Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
(21/02/20) EDIT: This answer is old and the other answers contains cross platform solutions so you should look at theirs first and treat this as a fallback solution.
The firebase auth plugin doesn't really have a proper cross-platform error code system yet so you have to handle errors for android and ios independently.
I'm currently using the temporary fix from this github issue: #20223
Do note since its a temp fix, don't expect it to be fully reliable as a permanent solution.
enum authProblems { UserNotFound, PasswordNotValid, NetworkError }
try {
FirebaseUser user = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password,
);
} catch (e) {
authProblems errorType;
if (Platform.isAndroid) {
switch (e.message) {
case 'There is no user record corresponding to this identifier. The user may have been deleted.':
errorType = authProblems.UserNotFound;
break;
case 'The password is invalid or the user does not have a password.':
errorType = authProblems.PasswordNotValid;
break;
case 'A network error (such as timeout, interrupted connection or unreachable host) has occurred.':
errorType = authProblems.NetworkError;
break;
// ...
default:
print('Case ${e.message} is not yet implemented');
}
} else if (Platform.isIOS) {
switch (e.code) {
case 'Error 17011':
errorType = authProblems.UserNotFound;
break;
case 'Error 17009':
errorType = authProblems.PasswordNotValid;
break;
case 'Error 17020':
errorType = authProblems.NetworkError;
break;
// ...
default:
print('Case ${e.message} is not yet implemented');
}
}
print('The error is $errorType');
}
Expanding on the accepted answer I thought it's worth to mention that:
The firebase_auth plugin has AuthException.
As pointed out in this Github issue post you can have the same error codes for Android and iOS.
If you have this code in a non-UI layer you can use rethrow or better throw your own formatted exceptions and catch those at the UI level (where you'll know exactly the kind of error you'll get).
try {
AuthResult authResult = await FirebaseAuth.instance.signInWithCredential(credential);
// Your auth logic ...
} on AuthException catch (e) {
print('''
caught firebase auth exception\n
${e.code}\n
${e.message}
''');
var message = 'Oops!'; // Default message
switch (e.code) {
case 'ERROR_WRONG_PASSWORD':
message = 'The password you entered is totally wrong!';
break;
// More custom messages ...
}
throw Exception(message); // Or extend this with a custom exception class
} catch (e) {
print('''
caught exception\n
$e
''');
rethrow;
}
the exceptions can be handled using, FirebaseAuthException class.
Here's the code for login using email and password:
void loginUser(String email, String password) async {
try {
await _auth.signInWithEmailAndPassword(email: email, password:password);
} on FirebaseAuthException catch (e) {
// Your logic for Firebase related exceptions
} catch (e) {
// your logic for other exceptions!
}
You can use your own logic to handle the error, e.g show a alert dialog, etc.
Same can be done for creating a user.
in Auth Class have this function:
Future signUpWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
FirebaseUser user = result.user;
return user;
} catch (e) {
return e;
}
}
The catch error above returns a runTimeType of PlatformException and a PlatformException in flutter has 3 properties check here!
in your Dart file, implement this on button listeners:
String error = "";
dynamic result = await _auth.signUpWithEmailAndPassword(email, password);
if (result.runtimeType == PlatformException) {
if (result.message != null) {
setState(() {
error = result.message;
});
} else {
setState(() {
error = "Unknown Error";
});
}
}
So I faced this issue today and instead of hardcoding the error messages to display, I decided to use string manipulations and I managed to get the message.
The goal was to get the message (everything after ]).
Example: get this => Password should be at least 6 characters from this => [firebase_auth/weak-password] Password should be at least 6 characters.
So using the exception from the try-catch, I converted it to string first, then replaced the first 14 characters (from '[' to '/') with nothing, so I was left with weak-password] Password should be at least 6 characters.
Then the split function with ']' pattern to search the remaining string for the ']' symbol and split the whole string into two with the index of the ']' symbol as the pivot. This returns a list with two Strings; 'weak-password' and 'Password should be at least 6 characters'. Use the index 1 to get the second string which is the error message.
e.toString().replaceRange(0, 14, '').split(']')[1]
I manage the firebase auth exception with the exceptions codes of the version
firebase_auth: ^3.3.6
firebase_core: ^1.12.0
This is the code that work for me:
Future<void> loginWithEmailAndPassword({
required String email,
required String password,
}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
} on firebase_auth.FirebaseAuthException catch (e) {
switch (e.code) {
case "invalid-email":
//Thrown if the email address is not valid.
throw InvalidEmailException();
case "user-disabled":
//Thrown if the user corresponding to the given email has been disabled.
throw UserDisabledException();
case "user-not-found":
//Thrown if there is no user corresponding to the given email.
throw UserNotFoundException();
case "wrong-password":
throw PasswordExceptions();
//Thrown if the password is invalid for the given email, or the account corresponding to the email does not have a password set.
default:
throw UncknownAuthException();
}
}
}
And I create the exceptions to control the messeges to display in the UI later like this:
class AuthenticationException implements Exception {}
class InvalidEmailException extends AuthenticationException {}
class PasswordExceptions extends AuthenticationException {}
class UserNotFoundException extends AuthenticationException {}
class UserDisabledException extends AuthenticationException {}
class UncknownAuthException extends AuthenticationException {}
Hope it help to someone having problems to handle auth exceptions!
in Dart you can react to different Exceptions using the on syntax. Since Firebase uses its own PlatformException you can easily catch them with:
try {
AuthResult result = await signUp(email, password);
} on PlatformException catch (e) {
print(e.message);
} on Exception catch (e) {
print(e);
}
PlatformException brings a code as well as a message which can be displayed in the UI, i.e. :
PlatformException(ERROR_EMAIL_ALREADY_IN_USE, The email address is already in use by another account., null)
I was stuck on this for a while too, i created this gist with all the available errors here with an example, covers all the platform exception codes
Example for handling sign up exceptions
Future<String> signUp(String email, String password, String firstName) async {
FirebaseUser user;
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
user = result.user;
name = user.displayName;
email = user.email;
Firestore.instance.collection('users').document(user.uid).setData({
"uid": user.uid,
"firstName": firstName,
"email": email,
"userImage": userImage,
});
} catch (error) {
switch (error.code) {
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Anonymous accounts are not enabled";
break;
case "ERROR_WEAK_PASSWORD":
errorMessage = "Your password is too weak";
break;
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email is invalid";
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
errorMessage = "Email is already in use on different account";
break;
case "ERROR_INVALID_CREDENTIAL":
errorMessage = "Your email is invalid";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
Example for handling sign in exceptions
Future<String> signIn(String email, String password) async {
FirebaseUser user;
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = result.user;
name = user.displayName;
email = user.email;
userId = user.uid;
} catch (error) {
switch (error.code) {
case "ERROR_INVALID_EMAIL":
errorMessage = "Your email address appears to be malformed.";
break;
case "ERROR_WRONG_PASSWORD":
errorMessage = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
errorMessage = "User with this email doesn't exist.";
break;
case "ERROR_USER_DISABLED":
errorMessage = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
errorMessage = "Too many requests. Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
errorMessage = "Signing in with Email and Password is not enabled.";
break;
default:
errorMessage = "An undefined Error happened.";
}
}
if (errorMessage != null) {
return Future.error(errorMessage);
}
return user.uid;
}
I prefer to create api layer response and error models and wrap the firebase plugin error and response objects in them. For sign in with email and password i have this
#override
Future<dynamic> loginWithEmailAndPassword(String email, String password) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return FirebaseSignInWithEmailResponse();
} catch (exception) {
return _mapLoginWithEmailError(exception);
}
}
ApiError _mapLoginWithEmailError(PlatformException error) {
final code = error.code;
if (code == 'ERROR_INVALID_EMAIL') {
return FirebaseSignInWithEmailError(
message: 'Your email is not valid. Please enter a valid email',
type: FirebaseSignInWithEmailErrorType.INVALID_EMAIL);
} else if (code == 'ERROR_WRONG_PASSWORD') {
return FirebaseSignInWithEmailError(
message: 'Your password is incorrect',
type: FirebaseSignInWithEmailErrorType.WRONG_PASSWORD);
} else if (code == 'ERROR_USER_NOT_FOUND') {
return FirebaseSignInWithEmailError(
message: 'You do not have an account. Please Sign Up to'
'proceed',
type: FirebaseSignInWithEmailErrorType.USER_NOT_FOUND);
} else if (code == 'ERROR_TOO_MANY_REQUESTS') {
return FirebaseSignInWithEmailError(
message: 'Did you forget your credentials? Reset your password',
type: FirebaseSignInWithEmailErrorType.TOO_MANY_REQUESTS);
} else if (code == 'ERROR_USER_DISABLED') {
return FirebaseSignInWithEmailError(
message: 'Your account has been disabled. Please contact support',
type: FirebaseSignInWithEmailErrorType.USER_DISABLED);
} else if (code == 'ERROR_OPERATION_NOT_ALLOWED') {
throw 'Email and Password accounts are disabled. Enable them in the '
'firebase console?';
} else {
return FirebaseSignInWithEmailError(
message: 'Make sure you have a stable connection and try again'
type: FirebaseSignInWithEmailErrorType.CONNECTIVITY);
}
}
I never return the AuthResult from firebase. Instead i listen to the onAuthStateChanged stream and react accordingly if there is a change.
I have the same error of "firebse platform exeption:" in flutter using "Firebase auth"
and it didn't resolve even using try catch and trim() method in passing arrguments.
The problem is when you run app using Button "Run" in main.dart it won't callback and catch the error.
Solution: In Vscode terminal type "Flutter run" (for debug mode). or "Flutter run --release" (for release mode) now you won't face platform exception.
I had an issue where I didn't want the "com.google.firebase.FirebaseException: An internal error has occurred. [ Unable to resolve host "www.googleapis.com":No address associated
with hostname ]" which would indicate to a user that the backend being used is firebase. Thus, I just used toString().replaceAll()
Future<void> signIn() async {
final formState = _formkey.currentState;
var _date = DateTime.now();
if (formState!.validate()) {
emailFocus!.unfocus();
passwordFocus!.unfocus();
formState.save();
setState(() {
isloading = true;
_errorMessage = '';
});
try {
UserCredential user = await _firebaseAuth.signInWithEmailAndPassword(
email: _email, password: _password!);
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', _email);
await FirebaseFirestore.instance
.collection('Users Token Data')
.doc(user.user!.uid)
.set({'Email': _email, 'Token': _token, 'Date': _date});
Navigator.pushNamedAndRemoveUntil(
context, RouteNames.homePage, (e) => false);
} on FirebaseAuthException catch (e) {
setState(() {
isloading = false;
_errorMessage = e.message.toString().replaceAll(
'com.google.firebase.FirebaseException: An internal error has' +
' occurred. [ Unable to resolve host "www.googleapis.com":' +
"No address associated with hostname ]",
"Please Check Network Connection");
});
print(e.message);
}
}
}
}
Just incase if you don't wanna reveal too much information from the error message.
I have also recently faced this error and, I have found out that the .catchError() callback wasn't being called in the debug mode (which is when you click the Run->Start Debugging button in VSCode).
However, when you type in flutter run -d , then, the .catchError() method gets called back as it is not in debug mode.
To get your preferred simulator's code paste this line of code in the terminal:
instruments -s devices
If that doesn't work, you can also try pasting this:
xcrun simctl list
The ```.catchError()`` method will get called unlike before and the code inside that will get executed as expected!
Additionally, the app won't crash anymore with a PlatformException() and instead you will get a log like this one:
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'uid' was called on null.
Receiver: null
I have been facing this problem in Google Sign In too, in which the .catchError() was not being called!
In conclusion, if you have some error with handling errors in Firebase Authentication, you should first try to first run in through the terminal. Thanks, and I hope this helps!
try this , i had the same proplem and this code worked with me
catch (e) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Wrong UserName or Password')));
}
Firebase-Auth Latest version provides a simple way to get error code and error Messages using the following try catch statements:
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: "name#example.com",
password: "SuperSecretPassword!"
);
} on FirebaseAuthException catch (e) {
print('Failed with error code: ${e.code}');
print(e.message);
}
Source:
Firebase Documentation
This code will show a red snack bar that contains the exception cause like "User already in use by other account".
auth.createUserWithEmailAndPassword(
email: email,
password: password,
).onError((error,stackTrace){
if(error.runtimeType == FirebaseAuthException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(error.toString().replaceRange(0, 14, '').split(']')[1]),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
}
try {
final newuser = await _auth.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text);
// print("Done");
} catch (e) {
print(e);
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(e.message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}

Resources