Authenticating users with Firebase and Flutter

When it comes to building apps, it’s likely that you’re going to want to perform some sort of user authentication, data storage or some other related tasks. Luckily for us, there are a collection of tools available from Firebase that allow us to achieve such things — some of these tools are also available for use with Flutter in the form of packages. In this post, I want to take a quick look at how we can perform authentication in our Flutter applications using Firebase.


We need to begin by adding the Firebase Auth package to our pubspec.yaml file, once you’ve done so you can run Get Packages to pull the package down into your project so that it is available for use:

firebase_auth: “0.5.12”

Now that we have this in our project we can go ahead and perform some authorization in our app. Now, we’re just going to look at the authorization flow here — we’re not going to create all of the different widgets here that are required but let’s imagine we have four different classes available:

  • An app class, which will return the instance of the MaterialApp for use by our application
  • A splash screen widget, which will handle a loading state whilst the signed in status is checked
  • A login screen widget, which will be shown when there is no authenticated user
  • A main screen widget, which is the screen shown when the user is authenticated

Our app class is the one that is launched when our application is started, so this is where we are going to place the logic that is used to handle the authenticated state.

return new MaterialApp(
  title: “Your app name”,
  theme: AppTheme.theme,
  home: _handleCurrentScreen(),
);

Here you’ll notice that we’re using the home property of the MaterialApp instance that we’re using. We’re not using routes here as we only have a simple navigation structure for this example, but the home property is essentially the widget to be used for the default root “/” path of our application. Here, we’re using a function called _handleCurrentScreen() to control the widget that is going to be displayed to the user when our application is launched:

Widget _handleCurrentScreen() {
    return new StreamBuilder<FirebaseUser>(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (BuildContext context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return new SplashScreen();
        } else {
          if (snapshot.hasData) {
            return new MainScreen(firestore: firestore,
                uuid: snapshot.data.uid);
          }
          return new LoginScreen();
        }
      }
    );
}

Essentially what we are doing here is using the auth state change listener from the FirebaseAuth package, observing its value and then setting the displayed widget based off of what is received here. Let’s break this function down a little bit:

  • We begin by creating a new instance of the StreamBuilder class. This is a widget that uses the given stream to build itself, so as the data of the stream changes our UI can be updated accordingly. We declare the type that the stream is going to be passing into the builder in the form of a Firebase User instance — this is the type of the data inside the recieved snapshot that we will emitted here.
  • For the stream property of our stream builder, we are making use of the onAuthStateChange Stream that the Firebase Auth package provides. This stream allows us to receive events of the type FisebaseUser whenever the authorization status is changed, ie. signed in or signed out.
  • Now each time the stream emits a new snapshot we can use this to return a widget based off of the state of this. Now, the snapshot will contain the declared data type as well as a ConnectionState. This state will either be active, done, waiting, values or none. From the stream builder above, this is the logic we used whenever a new snapshot is emitted:
if (snapshot.connectionState == ConnectionState.waiting) {
    return new SplashScreen();
} else {
    if (snapshot.hasData) {
        return new MainScreen();
    }
    return new LoginScreen();
}
  • Here we begin by checking if we’re currently waiting for a stream event. If the state is currently waiting, then we return our splash screen instance. This is because we’ve connected to the stream but we’re waiting for some interaction from it and because of this, we need to show some form of splash or progress to the user.
  • Next, if we’re not in the waiting state then we need to show a screen based on whether or not there is data in the received snapshot. Using the hasData property of the snapshot we can decide whether we show the main screen of the application or request for the user to login.

At this point we have a straightforward way of showing the relevant screen based on whether or not the user is currently authenticated. But what if they aren’t authenticated? In this case we need to actually allow the user to authenticate, so for this we’re going to again use the Firebase Auth package to implement this functionality.


To do this we’re going to make use of FirebaseAuth and the Google Sign In functionality that it provides. This allows us to trigger a google authentication flow, allowing our user to sign-up / sign-in using their chosen Google account. We’re not going to implement this whole flow here, instead just the function that would be called when some button press takes place to trigger the authentication flow. For this we’ll create a function called _authenticateWithGoogle().

GoogleSignIn googleSignIn ...;
void _authenticateWithGoogle() async {
final GoogleSignInAccount googleUser = await googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = 
await googleUser.authentication;
final FirebaseUser user = await
widget.firebaseAuth.signInWithGoogle(
    accessToken: googleAuth.accessToken,
    idToken: googleAuth.idToken,
  );
  // do something with signed-in user
}

What happens here? Well, let’s break down each line of the function:

  • We begin by using the creating an instance of the GoogleSignIn class. This may be passed in from a parent widget or instantiated from the widget being used. This class provides us with a collection of functions, but here we’re just going to make use of the signIn() function.
  • In our _authenticateWithGoogle() we begin by making a call to this signIn() function. This is an asynchronous process and returns us a Future with the type of GoogleSignInAccount. When we call this function the user is presented with a dialog to select the Google account which they wish to authenticate with. When the user has selected an account and the request completed, we will be returned with the GoogleSignInAccount instance that we will use to authenticate the user with Firebase. If the user cancels the flow at any point, we will simply be returned a null value.
  • Next we need to get the instance of the GoogleSignInAuthentication — his holds a reference to the authenticated user and access token for use by our application. Again, this is an asynchronous task so we are returned a future for this type.
  • Now that we have all of the information around the user that we require, we can go ahead and call the signInWithGoogle() from our FirebaseAuth instance. When we call this we must pass in two arguments — the accessToken and idToken that we retrieved from the previous steps. This is an asynchronous function and will return us a Future of the type FirebaseUser — this extends the UserInfo class and contains all of the information that we require for an authenticated user.

At this point our user is now authenticated and we have access to the authenticated user from our signInWithGoogle() call. If we need to retrieve the authenticated user at any point then we can do so by calling the currentUser() function on our FirebaseAuth instance.


So now if we go back to the start of this article where we defined this function:

Widget _handleCurrentScreen() {
    return new StreamBuilder<FirebaseUser>(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (BuildContext context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return new SplashScreen();
        } else {
          if (snapshot.hasData) {
            return new MainScreen(firestore: firestore,
                uuid: snapshot.data.uid);
          }
          return new LoginScreen();
        }
      }
    );
}

When we carry out the authentication flow that we just implemented, the onAuthStateChange callback will be triggered and our StreamBuilder will receive this data and rebuild our widget based on the current authentication state. The same will happen here if the user decides to sign out and some point.


From this I hope I’ve been able to show how we can easily add authentication to our Flutter applications using the Firebase Authentication plugin. If you have any thoughts or questions then please do reach out and I’ll be happy to help out!