image

01 Apr 2026

9K

35K

Flutter & Firebase Auth: Implementing a Custom Password Reset Flow with Custom UI and Email Verification

User authentication is a cornerstone of almost every modern application. While robust, Flutter offers excellent flexibility to build beautiful and intuitive user interfaces, and Firebase Authentication provides a secure, scalable, and easy-to-implement backend. A critical feature in any authentication system is the ability for users to reset their forgotten passwords. This article will guide you through building a custom password reset flow in Flutter using Firebase Authentication, complete with a tailored UI and an understanding of how email verification plays a role in the security of this process.

Understanding the Password Reset Flow

The standard password reset flow involves several steps:

  1. The user navigates to a "Forgot Password" screen in your Flutter app.
  2. They enter their registered email address.
  3. Your Flutter app sends a password reset request to Firebase Authentication.
  4. Firebase sends a unique password reset link to the user's email address.
  5. The user opens the email and clicks the link, which typically directs them to a secure Firebase-hosted page (or a custom page if advanced deep linking is configured) where they can set a new password.
  6. Once the password is reset, the user can log in with their new credentials.

In this context, "Email Verification" is crucial. When a password reset email is sent, Firebase ensures that the email address corresponds to an existing user account. By delivering the reset link to the registered email, Firebase effectively verifies the user's ownership of that email account, thus protecting the account from unauthorized password changes. Firebase also provides settings to customize the content and branding of these reset emails.

Prerequisites

  • Flutter SDK installed and configured.
  • A Firebase project set up with Firebase Authentication enabled (specifically, the Email/Password sign-in method).
  • Your Flutter project connected to your Firebase project (e.g., with google-services.json for Android and GoogleService-Info.plist for iOS).

Step-by-Step Implementation

1. Firebase Project Setup

Ensure your Firebase project has the Email/Password provider enabled under "Authentication" > "Sign-in method". You can also customize the password reset email template (sender name, subject, body, and action URL) in the "Templates" tab within the Authentication section of the Firebase Console.

2. Flutter Project Setup: Adding Dependencies

First, add the necessary Firebase packages to your pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  firebase_core: ^latest_version
  firebase_auth: ^latest_version

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^latest_version

Run flutter pub get to fetch these packages.

3. Building the Custom UI for "Forgot Password"

Create a dedicated screen for the user to initiate the password reset. This will typically include a text field for the email address and a button to send the reset email.

Create a new file, e.g., lib/forgot_password_screen.dart:


import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class ForgotPasswordScreen extends StatefulWidget {
  const ForgotPasswordScreen({super.key});

  @override
  State createState() => _ForgotPasswordScreenState();
}

class _ForgotPasswordScreenState extends State {
  final _emailController = TextEditingController();
  final _formKey = GlobalKey();
  bool _isLoading = false;

  @override
  void dispose() {
    _emailController.dispose();
    super.dispose();
  }

  Future<void> _sendPasswordResetEmail() async {
    if (_formKey.currentState!.validate()) {
      setState(() {
        _isLoading = true;
      });
      try {
        await FirebaseAuth.instance.sendPasswordResetEmail(
          email: _emailController.text.trim(),
        );
        if (!mounted) return;
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Password reset link sent! Check your email.'),
            backgroundColor: Colors.green,
          ),
        );
        // Optionally navigate back to login screen
        Navigator.of(context).pop();
      } on FirebaseAuthException catch (e) {
        if (!mounted) return;
        String message;
        switch (e.code) {
          case 'user-not-found':
            message = 'No user found for that email.';
            break;
          case 'invalid-email':
            message = 'The email address is not valid.';
            break;
          default:
            message = 'An unexpected error occurred: ${e.message}';
        }
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(message),
            backgroundColor: Colors.red,
          ),
        );
      } catch (e) {
        if (!mounted) return;
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('An unexpected error occurred: $e'),
            backgroundColor: Colors.red,
          ),
        );
      } finally {
        setState(() {
          _isLoading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Reset Password'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'Enter your email to receive a password reset link.',
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 20),
              TextFormField(
                controller: _emailController,
                keyboardType: TextInputType.emailAddress,
                decoration: InputDecoration(
                  labelText: 'Email',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.email),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter your email';
                  }
                  if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
                    return 'Please enter a valid email address';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 20),
              _isLoading
                  ? const CircularProgressIndicator()
                  : ElevatedButton(
                      onPressed: _sendPasswordResetEmail,
                      style: ElevatedButton.styleFrom(
                        minimumSize: const Size(double.infinity, 50),
                      ),
                      child: const Text('Send Reset Link'),
                    ),
              const SizedBox(height: 10),
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop(); // Go back to login screen
                },
                child: const Text('Back to Login'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

4. Integrating the Password Reset Logic

The core logic resides within the _sendPasswordResetEmail method. It uses FirebaseAuth.instance.sendPasswordResetEmail to initiate the process. Error handling is crucial to provide feedback to the user.

  • We wrap the Firebase call in a try-catch block to handle potential FirebaseAuthException errors (e.g., `user-not-found`, `invalid-email`).
  • On success, a SnackBar notifies the user that the link has been sent.
  • On failure, a different SnackBar displays the specific error message.
  • A loading indicator (`CircularProgressIndicator`) is shown while the request is being processed.

5. Navigating to the Forgot Password Screen

From your login screen, you can add a "Forgot Password?" text button that navigates to your new ForgotPasswordScreen:


// In your LoginScreen widget's build method
TextButton(
  onPressed: () {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => const ForgotPasswordScreen(),
      ),
    );
  },
  child: const Text('Forgot Password?'),
),

Conclusion

Implementing a custom password reset flow in Flutter with Firebase Authentication is straightforward and significantly enhances the user experience of your application. By leveraging Firebase's robust backend and Flutter's flexible UI capabilities, you can provide a seamless and secure way for users to regain access to their accounts. Remember that the "Email Verification" aspect within this flow is implicitly handled by Firebase, ensuring that the reset link is securely delivered to the rightful owner's registered email address.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is