Authentication Error Handling in Flutter
Introduction
In this blog, we shall discuss about handing authentication exception. When the user signs in or signup he might get some error but if we do not handle those errors the user will not be able to know about the authentication failure. So it is very important to handle those errors and display a message or alert so that the user can change his credentials as per the requirement of the error.
Table of contents:
Handle Firebase Auth exceptions
Validating TextField
Email TextField
TextFormField(
key: ValueKey('email'),
autocorrect: false,
textCapitalization: TextCapitalization.none,
enableSuggestions: false,
validator: (value) {
if (value.isEmpty || !value.contains('@')) {
return 'Please enter a valid email address.';
}
return null;
},
keyboardType: TextInputType.emailAddress,
)
: TextFormField provides us validator property to show an error if the email is empty or it does not contain @. You can add as many error messages you want using nested if-else.
Password TextField
TextFormField(
key: ValueKey('password'),
validator: (value) {
if (value.isEmpty || value.length < 7) {
return 'Password must be at least 7 characters long.';
}
return null;
},
obscureText: hidePassword,
)
Handle Platform exception
This handle the error of different platforms such as android and ios
try {
if (isLogin) {
userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else if (password == confirmPassword) {
userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} else {}
} on PlatformException catch (err) {
authProblems errorType;
Scaffold.of(ctx).showSnackBar(
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');
}
}
SnackBar(
content: Text($errorType),
backgroundColor: Theme.of(ctx).errorColor,
),
);}
Handling both platform error using a single code
var errorMessage;
final _auth = FirebaseAuth.instance;
try {
if (isLogin) {
userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else if (password == confirmPassword) {
userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
}
} on PlatformException catch (err) {
var message = 'An error occurred, please check your credentials!';
if (err.message != null) {
message = err.message;
setState(() {
errorMessage = message;
});
print(message);
}
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Theme.of(ctx).errorColor,
),
);
}
Here we are directly displaying the error message using a snack bar. If the error message is null then ‘An error occurred, please check your credentials!’
message will be displayed. If an error message is not null then message = err.message;
.
Handle Firebase Auth exceptions
catch (error) {
print(error);
setState(() {
errorMessage = error.toString();
});
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(errorMessage),
backgroundColor: Colors.red,
),
);
}
While logging in user can get the following error :
- [firebase_auth/wrong-password] The password is invalid or the user does not have a password.
- [firebase_auth/invalid-email] The email address is badly formatted.
- [firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.
While Signing up user can get the following error:
- [firebase_auth/email-already-in-use] The email address is already in use by another account.
- [firebase_auth/invalid-email] The email address is badly formatted.
- [firebase_auth/weak-password] Password should be at least 6 characters.
Complete auth module (Ready to use)
Complete Dart Code File for Auth Form :
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:laundary_application/widgets/userImagePicker.dart';
class AuthForm extends StatefulWidget {
AuthForm(
this.submitFn,
this.isLoading,
);
final bool isLoading;
final void Function(
String email,
String password,
String confirmPassword,
String userName,
File image,
bool isLogin,
BuildContext ctx,
) submitFn;
@override
_AuthFormState createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
final _formKey = GlobalKey<FormState>();
var _isLogin = true;
var _userEmail = '';
var _userName = '';
var _userPassword = '';
var _confirmPassword = '';
File _userImageFile;
bool hidePassword = true;
void _pickedImage(File image) {
_userImageFile = image;
}
void _trySubmit() {
final isValid = _formKey.currentState.validate();
FocusScope.of(context).unfocus();
if (_userImageFile == null && !_isLogin) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Please pick an image.'),
backgroundColor: Theme.of(context).errorColor,
),
);
return;
}
if (isValid) {
_formKey.currentState.save();
widget.submitFn(
_userEmail.trim(),
_userPassword.trim(),
_confirmPassword.trim(),
_userName.trim(),
_userImageFile,
_isLogin,
context,
);
}
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return Center(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
!_isLogin
? SizedBox(
height: 0,
)
: Image.asset(
"assets/appIcon.png",
height: height * 0.2,
),
if (!_isLogin) UserImagePicker(_pickedImage, widget.isLoading),
Container(
width: width,
height: 50,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey.withOpacity(0.5),
width: 1,
),
borderRadius: BorderRadius.circular(5),
),
child: TextFormField(
key: ValueKey('email'),
autocorrect: false,
textCapitalization: TextCapitalization.none,
enableSuggestions: false,
validator: (value) {
if (value.isEmpty || !value.contains('@')) {
return 'Please enter a valid email address.';
}
return null;
},
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'Email address',
prefixIcon: Icon(Entypo.mail),
border: InputBorder.none),
onSaved: (value) {
_userEmail = value;
},
),
),
SizedBox(height: 12),
if (!_isLogin)
Container(
width: width,
height: 50,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey.withOpacity(0.5),
width: 1,
),
borderRadius: BorderRadius.circular(5),
),
child: TextFormField(
key: ValueKey('username'),
autocorrect: true,
textCapitalization: TextCapitalization.words,
enableSuggestions: false,
validator: (value) {
if (value.isEmpty || value.length < 4) {
return 'Please enter at least 4 characters';
}
return null;
},
decoration: InputDecoration(
hintText: 'Username',
prefixIcon: Icon(Icons.edit),
border: InputBorder.none,
),
onSaved: (value) {
_userName = value;
},
),
),
SizedBox(height: 12),
Container(
width: width,
height: 50,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey.withOpacity(0.5),
width: 1,
),
borderRadius: BorderRadius.circular(5),
),
child: TextFormField(
key: ValueKey('password'),
validator: (value) {
if (value.isEmpty || value.length < 7) {
return 'Password must be at least 7 characters long.';
}
return null;
},
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(
hidePassword ? Entypo.eye_with_line : Entypo.eye),
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
),
hintText: 'Password',
border: InputBorder.none,
prefixIcon: Icon(
Icons.vpn_key,
)),
obscureText: hidePassword,
onSaved: (value) {
_userPassword = value;
},
),
),
SizedBox(height: 12),
if (!_isLogin)
Container(
width: width,
height: 50,
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey.withOpacity(0.5),
width: 1,
),
borderRadius: BorderRadius.circular(5),
),
child: TextFormField(
key: ValueKey('password'),
validator: (value) {
if (value.isEmpty || value.length < 7) {
return 'Password must be at least 7 characters long.';
}
return null;
},
decoration: InputDecoration(
hintText: 'Confirm Password',
border: InputBorder.none,
prefixIcon: Icon(
Icons.vpn_key,
)),
obscureText: hidePassword,
onSaved: (value) {
_confirmPassword = value;
},
),
),
SizedBox(height: 12),
widget.isLoading
? CircularProgressIndicator()
: InkWell(
onTap: _trySubmit,
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.pinkAccent.withOpacity(0.8),
borderRadius: BorderRadius.circular(5),
),
width: width,
child: Center(
child: Text(
_isLogin ? 'Login' : 'Sign Up',
style: GoogleFonts.lato(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w900,
),
)),
),
),
if (!widget.isLoading)
FlatButton(
child: RichText(
text: TextSpan(children: [
TextSpan(
text: _isLogin
? "Don't have an account? "
: "Already have an account?",
style: GoogleFonts.lato(
color: Colors.black54,
)),
TextSpan(
text: _isLogin ? "Sign Up" : "Log In",
style: GoogleFonts.lato(
color: Colors.red,
fontWeight: FontWeight.w900,
)),
]),
),
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
),
Image.asset(
"assets/demo1.png",
height: height * 0.26,
),
],
),
),
),
),
);
}
}
Let us Have a look at the authentication dart file :
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:laundary_application/widgets/authForm.dart';
class AuthScreen extends StatefulWidget {
@override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _auth = FirebaseAuth.instance;
var _isLoading = false;
var errorMessage;
void _submitAuthForm(
String email,
String password,
String confirmPassword,
String username,
File image,
bool isLogin,
BuildContext ctx,
) async {
UserCredential userCredential;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else if (password == confirmPassword) {
userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
final ref = FirebaseStorage.instance
.ref()
.child('user_image')
.child(userCredential.user.uid + '.jpg');
await ref.putFile(image).onComplete;
final url = await ref.getDownloadURL();
await FirebaseFirestore.instance
.collection('users')
.doc(userCredential.user.uid)
.set({
'username': username,
'email': email,
'image_url': url,
});
} else {
setState(() {
_isLoading = false;
errorMessage = "password does not match";
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(errorMessage),
backgroundColor: Colors.red,
),
);
});
}
} on PlatformException catch (err) {
var message = 'An error occurred, please check your credentials!';
if (err.message != null) {
message = err.message;
setState(() {
errorMessage = message;
});
print(message);
}
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Theme.of(ctx).errorColor,
),
);
setState(() {
_isLoading = false;
});
} catch (error) {
print(error);
setState(() {
errorMessage = error.toString();
});
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(errorMessage),
backgroundColor: Colors.red,
),
);
setState(() {
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: AuthForm(
_submitAuthForm,
_isLoading,
),
),
);
}
}
If you find anything that could be improved please let me know, I would love to improve.💙
If this article has helped you a bit and found interesting please clap!👏
Feel free to connect with us:
And read more articles from FlutterDevs.com
FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire flutter developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! You can connect with us on Facebook, GitHub, Twitter, and LinkedIn for any flutter related queries.
We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences!.