User authentication is a very common requirement for a lot of apps.
In this article we implement a simple authentication flow in Flutter, in less than 100 lines of code.
As part of this, we will see how to:
- use FirebaseAuth to sign in anonymously.
StreamBuilderto present different screens depending on the authentication status of the user.
This is the basis of my Reference Authentication Flow with Flutter & Firebase on GitHub.
So, let’s start from the basics.
We will use Firebase Authentication for this example.
After creating a new Flutter project, we can add
firebase_auth to the dependencies section of our
// pubspec.yaml dependencies: flutter: sdk: flutter firebase_auth: 0.11.1+3
Then, we need to configure our Flutter app to use Firebase. This guide explains what to do step-by-step:
The two most important steps are:
google-services.jsonto the iOS and Android projects, otherwise the app will crash at startup.
- Enable anonymous sign-in in the Firebase console, as we will use it in this example.
I cover all these steps in detail in my Flutter & Firebase course.
Our application will have two pages, called
HomePage, which are both stateless widgets:
Then we will have another widget called
LandingPage. We will use this to decide which page to show depending on the authentication status of the user.
Here is the entire widget tree of this app:
Let’s implement this in code.
All this does is to show a centered
RaisedButton, which calls
_signInAnonymously() when pressed.
This method calls
FirebaseAuth.instance.signInAnonymously() and awaits for the result.
try/catchis used to catch any exceptions. We can use this to alert the user if sign-in fails.
await FirebaseAuth.instance.signInAnonymously()returns a
FirebaseUser, but our code doesn’t use the return value. That’s because we will handle the authentication status of the user somewhere else.
Speaking of which…
We use this widget class to decide which page to show:
This page uses two main ingredients:
FirebaseAuth.instance.onAuthStateChangedstream. This receives a new value each time the user signs in or out.
FirebaseUser. This takes
onAuthStateChangedas an input stream, and calls the
builderwhen the stream is updated.
So when a call to
FirebaseAuth.instance.signInAnonymously() succeeds, a new
FirebaseUser is added to
As a result, the builder is called and we can extract the
snapshot.data. And we use this to decide which page to show:
Also note how we’re checking the
connectionState of the snapshot:
This can be any of four possible values:
When the application starts, the builder is first called with
ConnectionState.waiting. We can use this to show a centered
Once the authentication status is determined, the
active, and our builder is called again.
In summary, we have three possible authentication states:
- user signed-in
- user not signed-in
And this code is all we need to handle them:
This class is similar to the
This code calls
FirebaseAuth.instance.signOut() when the logout button is pressed.
On success, a
null value is added to
onAuthStateChanged. As a result, the builder in our
LandingPage is called again, and this time we return a
Almost there. Now we just need to update our
main.dart file, to pass the
LandingPage() to the
home argument of
All in all, this entire flow takes less than 100 lines of code.
Here is the code for the entire example:
This uses just a single
main.dart file. I advise to put widget classes in separate files in your own projects ;)
We have seen how to build a simple authentication flow with Firebase.
This example doesn’t use any fancy app architecture.
And sometimes, keeping things simple is a good idea. As Albert Einstein once said:
Everything Should Be Made as Simple as Possible, But Not Simpler — Albert Einstein
However, Einstein wasn’t a software developer. 😄
And the code I presented has two major drawbacks:
1) Global Access
HomePage all access
FirebaseAuth via the
instance singleton variable.
This is not recommended because the resulting code is not testable.
2) Direct use of FirebaseAuth
FirebaseAuth directly in our widgets is not a good idea.
This can cause problems if our application grows, and we decide to use a different authentication provider in the future.
In the next article we will see how to address these concerns. We will do this by:
- moving from global access to scoped access with Provider
- writing an authentication service class as a wrapper for
By the way, I cover all these topics (and much more) in my Flutter & Firebase Udemy course. This is available for early access at this link (discount code included):