How to implement Forgot Password/Password Reset into your Flutter Apps with Firebase Auth

dav4thevid

Fabusuyi David Oluwasegun

Posted on December 25, 2020

How to implement Forgot Password/Password Reset into your Flutter Apps with Firebase Auth

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: () {},
              )
            ],
          ),
        ),
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

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),
   ),
),

Enter fullscreen mode Exit fullscreen mode

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 thisawait _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);
}
Enter fullscreen mode Exit fullscreen mode

}
`

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);
}
Enter fullscreen mode Exit fullscreen mode

}

`

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 ConfirmEmailadd 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.

💖 💪 🙅 🚩
dav4thevid
Fabusuyi David Oluwasegun

Posted on December 25, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related