Mastering Firebase Services – Authentication and Crashlytics in a Flutter App

Firebase services have played a keen and vital role in terms of the Flutter framework. Due to its easy implementation techniques and code snippets. With this, offering a wide range of features, today we are going to have a deep look at the initial two; Authentication and Crashlytics features within our application. Let’s explore Mastering Firebase Services – Authentication and Crashlytics in a Flutter App.


Note: Before we jump into this blog post content:

Be sure to check out the latest Firebase integration via FlutterFire CLI from the link below:

To grab full-fledged knowledge regarding this database and to get the most out of it.


– Project Overview

Student Grading Application

Note: If you’ve gone through the above-mentioned blog post, which lists a Student Grading App,

We’re going to apply these features on the same platform since it’s going to reduce a lot of workloads.

– Firebase Auth

Auth (authentication), in general, covers the entire flow of applications from signup to OTP to verification and so on.

However, we’re going to perform a perfect Login and Registration process with this feature, and others upon deep dive.

— Implementation

Note: Be sure to select the correct Gmail account, otherwise you will end up struggling for the right project.

Under the Firebase Dashboard, Build section, check for the Authentication tab.

Firebase Auth

Here, onboard yourself with it, you should see Add new Provider. Our main task starts here.

You shall see a thorough listing of different providers with whom Firebase could be connected.

For the sake of simplicity, choose Email/Password, and enable the first switch button.

Auth providers

Let’s now move to our Flutter project and add two additional screens for signup and login respectively.

Register screen
Login screen

Both would require the same input.

In the pubspec.yaml file, add firebase_auth plugin to enable all the associated services.

user_signup.dart:

Firstly, initialize FirebaseAuth and add this method:

  late FirebaseAuth auth;

  @override
  void initState() {
    // TODO: implement initState

    auth = FirebaseAuth.instance;
    super.initState();
  }


 signup() async {
    isLoading = true;
    await auth.createUserWithEmailAndPassword(email: emailC.text, password: 
    passC.text);

    if (auth.currentUser != null) {
      SgNavigation().pushAndRemove(const StudentGrading());
    }
    isLoading = false;
  }

On successful sign-up, you shall be able to see the record inserted under the users’ section in Auth (Firebase dashboard).

Auth data inserted

Moreover, this method will run fine, but will not give suitable output on failure, so let’s wrap it up with a try-catch approach and apply Firebase’s built-in exception:

Updated Code Snippet:

  signup() async {
    try {
      isLoading = true;
      await auth.createUserWithEmailAndPassword(email: emailC.text, password: 
      passC.text);

      if (auth.currentUser != null) {
        SgNavigation().pushAndRemove(const StudentGrading());
      }
      isLoading = false;
    }
    on FirebaseException catch (e) {
      if(e.code == 'account-exists-with-different-credential') {
        SgUtils().showSnackBar("User already registered");
        isLoading = false;
      }
      else if(e.code == 'ERROR_EMAIL_ALREADY_IN_USE') {
        SgUtils().showSnackBar("User already registered");
        isLoading = false;
      }
      else if(e.code == 'email-already-in-use') {
        SgUtils().showSnackBar("User already registered");
        isLoading = false;
      }
      setState(() {});
    }
    finally {
      isLoading = false;
      setState(() {});
    }
  }


/* Calling the method */

 isLoading
              ? const Center(
                  child: CircularProgressIndicator(),
                )
              : ElevatedButton(
                  onPressed: () {
                    setState(() {});
                    signup();
                  },
                  child: const Text("Signup")),

user_login.dart:

The process shall remain the same as above, so, here is the direct method overview.

  signIn() async {
    try {
      isLoading = true;
      await auth.signInWithEmailAndPassword(email: emailC.text, password: 
      passC.text);

      if (auth.currentUser != null) {
        SgNavigation().pushAndRemove(const StudentGrading());
      }
      isLoading = false;
    }
    on FirebaseException catch (e) {
      if(e.code == 'ERROR_WRONG_PASSWORD') {
        SgUtils().showSnackBar("Wrong Password");
        isLoading = false;
      }
      else if(e.code == 'ERROR_USER_NOT_FOUND') {
        SgUtils().showSnackBar("No user exists");
        isLoading = false;
      }
      else if(e.code == 'INVALID_LOGIN_CREDENTIALS') {
        SgUtils().showSnackBar("No user exists");
        isLoading = false;
      }
      setState(() {});
    }
    finally {
      isLoading = false;
      setState(() {});
    }
  }


/* Calling the method */

 isLoading
              ? const Center(
                  child: CircularProgressIndicator(),
                )
              : ElevatedButton(
                  onPressed: () {
                    setState(() {});
                    signIn();
                  },
                  child: const Text("Sign in")),

Secondly, one more thing we need to handle is:

Once a user is signed in, it returns to the respective screens.

In the app.dart file, where this snippet exists:

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      navigatorKey: SgNavigation.navigatorKey,
      home: UserSignup(),
    );
  }
}

Here is the updated & replaced version of it:

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  Stream<bool> isUserSignedIn() async* {
    User? auth = FirebaseAuth.instance.currentUser;  // Check if user's data 
    exists

    if(auth == null) {
      yield false;
    }
    else {
      yield true;
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      navigatorKey: SgNavigation.navigatorKey,
      home: StreamBuilder<bool>(
        initialData: false,
        stream: isUserSignedIn(),
        builder: (context, sp) {
          return sp.data! ? const StudentGrading() : const UserSignup();
        },
      )
    );
  }
}

Snippet for User-Signout:

await FirebaseAuth.instance.signOut();
Video Preview

— Other Stuff

Apart from this, there are various other built-in methods available, such as:

- Password change
- Sending email
- Verifying phone number and so on...

Let’s do it for a password change:

Add a new screen as change_password.dart and apply the following snippet:

Update Password screen
  late User? credential;

  @override
  void initState() {
    // TODO: implement initState

    credential = FirebaseAuth.instance.currentUser;
    super.initState();
  }


  updatePassword() async {
    try {
      isLoading = true;
      await credential!.updatePassword(passC.text);

      SgUtils().showSnackBar('Password updated successfully');
      SgNavigation().pushAndRemove(const UserSignIn());
      isLoading = false;
    }
    catch (e) {
      SgUtils().showSnackBar(e.toString());
      isLoading = false;
      setState(() {});
    }
    finally {
      isLoading = false;
      setState(() {});
    }
  }

Firebase Auth gets completed here, let’s now add a more creative and interesting concept of Crashlytics to our existing App.

– Firebase Crashlytics

Crashlytics, as the name has its answer, reports real-time app crashes when real users use your App. Furthermore, it provides quite useful ways to fix extensive bugs/crashes.

— Implementation

First, add firebase_crashlytics to pubspec.yaml.

You could make use of this command too:

flutter pub add firebase_crashlytics

to install the suitable version according to Flutter and Dark SDKs.

Next, be sure to jot down these two commands:

dart pub global activate flutterfire_cli
flutterfire configure

Note: The above-mentioned commands have been taken from the blog post that was mentioned at the start, having the title – Adding Firebase to Flutter App – A Latest Integration Overview.

This command ensures that Firebase services on the respective project are updated with new plugins, including Crashlytics.

Now, under the root method – main(), add the following snippets:

FlutterError.onError = (errorDetails) {
    FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
 };

This command passes all errors that are caught by the Flutter framework to Crashlytics.

PlatformDispatcher.instance.onError = (error, stack) {
   FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
    return true;
  };

This command passes all uncaught async errors that are not handled by Flutter to Crashlytics.

The Complete Code Snippet:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  
  FlutterError.onError = (errorDetails) {
    FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
  };
  
  PlatformDispatcher.instance.onError = (error, stack) {
    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
    return true;
  };
  runApp(const App());
}

Let’s now run the App and throw an exception intentionally.

Upon the App running, head over to the Crashlytics dashboard, Engage section. You shall see this view:

Crashlytics Dashboard
Throw an exception screen

On Callback:

We would be able to see the error that has been caught:

Error caught under crash.. dashboard

This is how the Firebase Crashlytics presents you with a surprise.

It’s time to wind up this blog post.

I hope you thoroughly enjoyed it!


Wrapping Up

Mastering Firebase Services – Authentication and Crashlytics in a Flutter App was all about exploring the entire flow of Auth vis-à-vis Crashlytics dashboard, implementation with the help of useful snippets, screenshots, etc.

However, if you think something was missed or presented wrongly, you can jot it down in the comments section below. I would love to help you.

Meanwhile, you can connect with me on the following:

GitHub – Click Here

LinkedIn – Click Here

Link to the Repository

Link to all other posts

Once again, thanks for your precious time!

Leave a Comment