Authentication State Management
The key to a professional authentication experience is automatically knowing if a user is logged in or out. You don't want your users to have to log in every time they open the app. Firebase Auth provides a powerful stream that tells you the current authentication state.
Why This Topic Matters
Managing the authentication state allows you to "protect" certain parts of your app. If a user is logged in, you show them the Home screen. If they are not, you show them the Login screen. By listening to authStateChanges(), you can make your app react instantly to sign-ins and sign-outs without manual navigation code everywhere.
How To Study This Chapter
This chapter introduces the StreamBuilder widget. A Stream is like a pipe through which data flows over time. StreamBuilder allows your UI to automatically rebuild whenever new data (a new auth state) comes through that pipe.
Listening to Auth State Changes
Firebase Auth provides a Stream<User?> via authStateChanges(). If the user is logged in, the stream emits a User object. If they are logged out, it emits null.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthWrapper extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
// 1. Check if the stream has an error
if (snapshot.hasError) {
return Center(child: Text('Something went wrong!'));
}
// 2. Check the connection state (loading)
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
// 3. Check if the user is logged in
if (snapshot.hasData) {
return HomeScreen(); // User is logged in
} else {
return LoginScreen(); // User is not logged in
}
},
);
}
}
Using the AuthWrapper
Instead of setting LoginScreen as your home in MaterialApp, you set the AuthWrapper.
void main() {
runApp(
MaterialApp(
home: AuthWrapper(),
),
);
}
Accessing User Info
Once logged in, you can access the user's information (like their email or UID) anywhere in your app using FirebaseAuth.instance.currentUser.
final user = FirebaseAuth.instance.currentUser;
if (user != null) {
print('Welcome, ${user.email}!');
}
Protecting Routes
While AuthWrapper handles the initial landing, you might also want to protect specific named routes. You can do this by checking the currentUser before navigating.
void _goToProfile() {
if (FirebaseAuth.instance.currentUser != null) {
Navigator.pushNamed(context, '/profile');
} else {
Navigator.pushNamed(context, '/login');
}
}
In the next chapter, we'll make our app even easier to use by adding Google Sign-In!