Firebase Authentication with Flutter: Complete Guide for Beginners

Firebase Authentication with Flutter: Complete Guide for Beginners

Firebase provides a set of tools that are generally implemented in the backend of any application and makes the task of developing applications very easy. We can authenticate, save data, images, and files, chat with people, and do machine-learning tasks very easily.

In this tutorial, we are going to learn, how to implement authentication using Firebase in Flutter. We will have a login page and a register page.

If you want to set up your Flutter application with Firebase, here is a complete guide.

https://www.allaboutflutter.com/flutter-firebase-tutorial-getting-started

Install dependency

The first step is to install the required dependencies. So add the firebase_core and firebase_auth into the dependencies.

The first step is to install the required dependencies. So add the firebase_core and firebase_auth into the dependencies.

  firebase_core: ^2.15.0
  firebase_auth: ^4.7.1

Save the pubspec.yaml file and if the dependencies are not fetched automatically, run the following command.

flutter pub get

Your dependencies will be installed.

Authentication State Logic

In Firebase, we can check the authentication state. We can detect it using

FirebaseAuth.instance.authStateChanges()

The function returns a Stream of User data. The user is implemented in FirebaseAuth and includes fields such as displayName, id, email, and emailVerified, as well as various methods like linkWithCredential and linkWithPhoneNumber.

Now we can also get a null User if the user is not authenticated. So we can redirect the user to login or register page if the user is not authenticated. Else the user will be redirected to home page.

So we can first check if the user is logged in or not using this code.

We are displaying a MaterialBanner.

_checkAuthenticated() async {
    FirebaseAuth.instance.authStateChanges().listen((User? user) {
      if (user == null) {
        ScaffoldMessenger.of(context).showMaterialBanner(
          MaterialBanner(
            content: const Text('User is currently signed out!'),
            actions: [
              TextButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).clearMaterialBanners();
                },
                child: const Text('Dismiss'),
              )
            ],
          ),
        );
      } else {
        ScaffoldMessenger.of(context).showMaterialBanner(
          MaterialBanner(
            content: const Text('User is signed in!'),
            actions: [
              TextButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).clearMaterialBanners();
                },
                child: const Text('Dismiss'),
              )
            ],
          ),
        );
      }
    });
  }

If we place the code on any page, we will see the following output.

Login Credential Logic

There are lots of ways to authenticate a user using Firebase. But for this tutorial, we will learn the simplest method, Email and Password authentication. Follow the steps given here and it will be the easiest way to do.

Step 1: Store the email and password in some variable

    String email = "example@gmail.com";
    String password = "1234";

I have taken some dummy text.

Step 2: Create a try catch block for safety because these functions are asynchronous and can throw error.

try {

}
catch (e) {
    log(e.toString());
}

Step 3: Take the instance of FirebaseAuth and call the signInWithEmailAndPassword() method. It takes email and password as parameters.

      FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );

Step 4: Now signInWithEmailAndPassword() is an asynchronous function, hence we need to await it. Also it returns UserCredential. We can store it as well.

UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );

      log('User ${userCredential.user!.uid} logged in');

Hence the user is successfully logged in.

Here is the full code.

Future<void> _login() async {
  String email = "example@gmail.com";
  String password = "1234";

  try {
    UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
      email: email,
      password: password,
    );

    log('User ${userCredential.user!.uid} logged in');
  }
  catch (e) {
    log(e.toString());
  }   
}

Register Credential Logic

Since we are performing authentication using email and password, we will perform registration using email and password as well. Now follow the steps here and it will be easiest.

Step 1: As usual, take the email and password.

String email = "example@gmail.com";
String password = "1234";

Step 2: Create a try-catch block.

try {

} catch (e) {
  log(e.toString());
}

Step 3: Take the FirebaseAuth instance and call the createUserWithEmailAndPassword() method which takes email and password.

FirebaseAuth.instance.createUserWithEmailAndPassword(
  email: email,
  password: password,
);

Step 4: Now createUserWithEmailAndPassword() is an asynchronous function, hence we need to await it. Also it returns UserCredential. We can store it as well.

UserCredential user = await FirebaseAuth.instance.createUserWithEmailAndPassword(
  email: email,
  password: password,
);

log('User ${user.user!.uid} registered');

Hence the registration is successful.

Here is the full code.

Future<void> _register() async {
  String email = "example@gmail.com";
  String password = "1234";

  try {
    UserCredential user =
        await FirebaseAuth.instance.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );

    log('User ${user.user!.uid} registered');
  } catch (e) {
    log(e.toString());
  }
}

Logout Logic

While authenticating user in Firebase is easy, logging out the user is even more easy. We just need to get the user and call signOut() method.

Future<void> _logout() async {
  try {
    await FirebaseAuth.instance.signOut();

  } catch (e) {
    log(e.toString());
  }
}

Login, Register and Home Page UIs

It is the time to create the UIs and integrate it in our main.dart file Auth function.

So the login, register page will contain a simple email ID and password text fields, whereas the HomePage will contain the user email and ID.

Login Page

Let's code the Login page. It will contain the following things.

  1. A TextEdittingController for email and another for password.

  2. A login function to authenticate with email and password.

  3. The UI itself.

Here is the code.

import 'dart:developer';

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

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

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  Future<void> _login() async {
    String email = _emailController.text;
    String password = _passwordController.text;

    try {
      UserCredential userCredential =
          await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );

      log('User ${userCredential.user!.uid} logged in');
    } catch (e) {
      log(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            const Text(
              'Login Page',
              style: TextStyle(
                fontSize: 48.0,
              ),
            ),

            // Email
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                controller: _emailController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Email',
                ),
              ),
            ),

            // Password
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                controller: _passwordController,
                obscureText: true,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Password',
                ),
              ),
            ),

            // login Button
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  _login();
                },
                child: const Text('Login'),
              ),
            ),

            TextButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => const RegisterPage(),
                  ),
                );
              },
              child: const Text('Not registered? Register here!'),
            ),
          ],
        ),
      ),
    );
  }
}

Output

Register Page

Let's code the Register page. It will contain the following things.

  1. A TextEdittingController for email, one for password and another for confirm password.

  2. A register function to register with email and password.

  3. The UI itself.

Here is the code.

import 'dart:developer';

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

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

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _confirmPasswordController =
      TextEditingController();

  Future<void> _register() async {
    String email = _emailController.text;
    String password = _passwordController.text;
    String confirmPassword = _confirmPasswordController.text;

    if (password != confirmPassword) {
      ScaffoldMessenger.of(context).showMaterialBanner(
        MaterialBanner(
          content: const Text('Passwords do not match!'),
          actions: [
            TextButton(
              onPressed: () {
                ScaffoldMessenger.of(context).clearMaterialBanners();
              },
              child: const Text('Dismiss'),
            )
          ],
        ),
      );
      return;
    }

    try {
      UserCredential user =
          await FirebaseAuth.instance.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );

      log('User ${user.user!.uid} registered');
      Navigator.pop(context);
    } catch (e) {
      log(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Register Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            const Text(
              'Register Page',
              style: TextStyle(
                fontSize: 48.0,
              ),
            ),

            // Email
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                controller: _emailController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Email',
                ),
              ),
            ),

            // Password
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                controller: _passwordController,
                obscureText: true,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Password',
                ),
              ),
            ),

            // Confirm Password
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                controller: _confirmPasswordController,
                obscureText: true,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: 'Confirm Password',
                ),
              ),
            ),

            // Register Button
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: () {
                  _register();
                },
                child: const Text('Register'),
              ),
            ),

            // Login Button
            TextButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => const LoginPage(),
                  ),
                );
              },
              child: const Text('Already registered? Login here!'),
            ),
          ],
        ),
      ),
    );
  }
}

Output

HomePage

The home page will be very simple. It will contain just the email address, user id and a logout method as we learnt above.

To get email address, id, etc. we just access the properties as a normal object we do.

User user = FirebaseAuth.instance.currentUser;
print(user.displayName)
print(user.uid)
print(user.email)

Here is the code.

import 'dart:developer';

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

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  User? user;
  // logout method
  Future<void> _logout() async {
    try {
      await FirebaseAuth.instance.signOut();
    } catch (e) {
      log(e.toString());
    }
  }

// get user
  void _getUser() {
    try {
      user = FirebaseAuth.instance.currentUser;
    } catch (e) {
      log(e.toString());
    }
  }

  @override
  void initState() {
    _getUser();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // AppBar
      appBar: AppBar(
        title: const Text('Home Page'),
      ),

      // Body
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            const Text(
              'Home Page',
              style: TextStyle(
                fontSize: 48.0,
              ),
            ),

            // Email
            Text(
              'Email: ${user?.email}',
              style: const TextStyle(
                fontSize: 24.0,
              ),
            ),

            // Password
            Text(
              'UID: ${user?.uid}}',
              style: const TextStyle(
                fontSize: 24.0,
              ),
            ),

            // Logout Button
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: ElevatedButton(
                onPressed: _logout,
                child: const Text('Logout'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Connect the UIs

In the main.dart file, where we are listening to the auth state change, we can also introduce a variable called loggedIn. If the user is logged in, we can display the login page. Else we will show the home page.

Here is the code.

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_auth_tutorial/home_page.dart';
import 'package:firebase_auth_tutorial/login_page.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AllAboutFlutter Login Tutorial',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
            seedColor: const Color.fromARGB(255, 0, 107, 195)),
        useMaterial3: true,
      ),
      debugShowCheckedModeBanner: false,
      home: const LoginTutorial(),
    );
  }
}

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

  @override
  State<LoginTutorial> createState() => _LoginTutorialState();
}

class _LoginTutorialState extends State<LoginTutorial> {
  bool loggedIn = false;
  _checkAuthenticated() async {
    FirebaseAuth.instance.authStateChanges().listen((User? user) {
      if (user == null) {
        setState(() {
          loggedIn = false;
        });
        ScaffoldMessenger.of(context).showMaterialBanner(
          MaterialBanner(
            content: const Text('User is currently signed out!'),
            actions: [
              TextButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).clearMaterialBanners();
                },
                child: const Text('Dismiss'),
              )
            ],
          ),
        );
      } else {
        setState(() {
          loggedIn = true;
        });
        ScaffoldMessenger.of(context).showMaterialBanner(
          MaterialBanner(
            content: const Text('User is signed in!'),
            actions: [
              TextButton(
                onPressed: () {
                  ScaffoldMessenger.of(context).clearMaterialBanners();
                },
                child: const Text('Dismiss'),
              )
            ],
          ),
        );
      }
    });
  }

  @override
  void initState() {
    super.initState();

    _checkAuthenticated();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login Tutorial'),
      ),
      body: Visibility(
        visible: !loggedIn,
        replacement: const HomePage(),
        child: const LoginPage(),
      ),
    );
  }
}

Output

Did you find this article valuable?

Support All About Flutter | Flutter and Dart by becoming a sponsor. Any amount is appreciated!