How to implement Forgot Password/Password Reset into your Flutter Apps with Firebase Auth
Fabusuyi David Oluwasegun
Posted on December 25, 2020
This is final series of our Step by Step Guide on How to Authenticate your Flutter App with Firebase Auth
You can check out the previous post here and here.
Adding Forgot password/Password functionality is very essential in every app, and with firebase_auth we already have all that done for us, all we have to do is call the method, pass the right arguments, then we are good to go.
TLDR
If you already familiar with how firebase works, feel free clone the complete project we would be building here
If you are a firebase beginner, I would highly recommend you code along. Let dive right in
FORGOT PASSWORD
Let’s start by creating our Forgot Password Screen
, open Screens folder and add a new file, name it ForgotPassword.dart
. import material library and add the code below.
class ForgotPassword extends StatelessWidget {
static String id = 'forgot-password';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlueAccent,
body: Form(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Email Your Email',
style: TextStyle(fontSize: 30, color: Colors.white),
),
TextFormField(
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: 'Email',
icon: Icon(
Icons.mail,
color: Colors.white,
),
errorStyle: TextStyle(color: Colors.white),
labelStyle: TextStyle(color: Colors.white),
hintStyle: TextStyle(color: Colors.white),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
),
SizedBox(height: 20),
RaisedButton(
child: Text('Send Email'),
onPressed: () {},
),
FlatButton(
child: Text('Sign In'),
onPressed: () {},
)
],
),
),
),
);
}
}
It’s just a basic UI, I won’t walk through that. Now let’s create our Forgot Password route, open main.dart
and add this code inside our routes, ForgotPassword.id: (context) => ForgotPassword(),
.
We also have to create a forgot password FlatButton
inside our SignIn class, and make it navigate to our newly created route.
Open SignIn Class add this code.
FlatButton(
onPressed: () {
Navigator.pushNamed(
context,
ForgotPassword.id,
);
},
child: Text(
'Forgot Password?',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
),
The above is doing nothing fancy, it’s just takes our newly created route id
then it passes that into Navigator.pushNamed
, and this would trigger navigation when the user presses this FlatButton
. Hot restart your app and test. forgot password should now take you to the Forgot Password screen.
Now that we have successfully added the UI and navigation for ForgotPassword
, lets implement the functionality.
Create a new method, name it passwordReset
, then we have to create an instance of firebase_auth, so we would be able to use its functions. import import 'package:firebase_auth/firebase_auth.dart';
like so, and create an instance of it with final _auth = FirebaseAuth.instance;
inside passwordReset
method. We now have an instance of firebase_auth with _auth
(i.e., any instance, variable or function with underscore, makes its private, that means we can't call it outside this class). Now add this line await _auth.sendPasswordResetEmail();
This takes an email as an argument. Create an email String above and pass it inside sendPasswordResetEmail' like this
await _auth.sendPasswordResetEmail(email: _email);`
We also need to prevent our code from moving to the next like when the auth request is not completed. Its an asynchronous call, we don't know how long it would take. if you are not familiar with asynchronous programming, you can check here for reference >>>. Add await to the instance and async to passwordReset
method. (whew, we almost there). Now wrap your password reset method with try and catch block, should in case the request wasn't successful, it would throw an exception, so your app doesn't break. Lastly add this line.
`
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmEmail();
}),
);
`
This would navigate us to our Confirm Email Screen on completion. (i.e. We created this in the previous article). Our password reset method should now look like this.
`
Future _passwordReset(String email) async {
try {
await _auth.sendPasswordResetEmail(email: email);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmEmail(
);
}
),
);
} catch (e) {
print(e);
}
}
`
Notice!!!, We have an error with the context, right? That is because our Class is a stateless widget, convert it into stateful widget by clicking on our ForgotPassword
class and press ctrl + .
.
Another way of fixing this is by calling our password reset method as a callback inside our RaisedButton
with the onPressed
property. Either way works, I would go with this (I don't like cluttering my UI with logic).
Now add onSaved
method inside the TextFormField
, add newEmail as its argument, then assign our _email
variable to newEmail
like is
`
onSaved: (newEmail) {
_email = newEmail;
},
`
Now call our passwordReset
inside the onPressed
property and pass our email variable like this.
onPressed: () {
_passwordReset();
print(_email);
},
Since we are using a TextformField
widget as out input field, we need to wrap our entire field into a form widget and pass its key, If we don't do that our TextFormField
would return null, because it’s doesn't know the current state of our form. We need to track the current state of our form. wrap the entire Column widget with a form widget. Form widget has a property key
add that with a _formKey
like this key: _formKey
. create a new variable formKey
below our email string variable like this. final _formKey = GlobalKey<FormState>();
. Now inside our passwordReset
, above sendPasswordReset function, add this this line. _formKey.currentState.save();
This would save the current state of our form and send the inputted values upon submission. Your passwordReset should look like this.
`
_passwordReset() async {
try {
_formKey.currentState.save();
await _auth.sendPasswordResetEmail(email: _email);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmEmail();
}),
);
} catch (e) {
print(e);
}
}
`
Run Hot restart and test your app. This should send a mail link to the provided email for resetting password and also navigate you to the confirm email screen.
(Note: remember you have to be registered before you can reset your password, since we are not handling errors, its won't prompt you or send you email if you are not registered, but you can check your terminal for invalid emails errors and so on).
Lastly, Notice our confirm email screen reads "'An email has just been sent to you, Click the link provided to complete registration'"
. That is not text we won't for password, lets make ConfirmEmail
text dynamic. (i.e another way of fixing this is by creating a separate screen for this).
Open ConfirmEmail
add a new variable below id String message
, add it inside the constructor
then cut the message text and pass the message inside our Text widget.
child: Text(
message, //here
Now open ForgotPassword
, create a new variable message, add the text you cut into this variable, then pass it inside our ConfirmEmail
. Your routing should look like this.
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmEmail(message: widget.message,);
}),
);
Do the same for our SignUp
Screen.
We have finally completed this series. I might be writing another post for error handling and form validations, if you want that, feel free to comment below. If you have any question, feel free to ask them also. you can correct me if you spot any errors or typos.
Thank you for taking your time to read this.
Posted on December 25, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.