You can watch the AuthGate switch respond to each user event in the Coding Liquids video walkthrough.
Model authentication with User and a stream
FirebaseAuth.instance owns the current session, currentUser is a snapshot, and authStateChanges emits sign-in and sign-out transitions. Drive route-level UI from the stream so automatic session restoration reaches the correct screen.
Checking currentUser once in main misses later changes and can race SDK restoration.
Create and sign in email users
createUserWithEmailAndPassword registers a new account, while signInWithEmailAndPassword authenticates an existing one. Trim email input, validate passwords before the call, and keep the returned UserCredential when profile setup follows.
Displaying raw FirebaseAuthException messages produces inconsistent and overly technical copy.
flutter pub add firebase_auth google_sign_in
Instagram’s auth-state map distinguishes session restoration, profile loading, verification, and signed-out UI.
Build an AuthGate with StreamBuilder
StreamBuilder
Starting a new authStateChanges listener inside nested pages duplicates routing decisions.

Build production-ready Firebase Authentication features
The Complete Flutter Guide turns firebase authentication into maintainable app architecture, polished UI, and testable production code.
Enrol nowEmail and Google authentication need both SDK packages listed in the application dependencies.
dependencies:
flutter:
sdk: flutter
firebase_auth: any
google_sign_in: any
Exchange Google identity for Firebase credentials
Initialise GoogleSignIn, authenticate the account, read its ID token, and create a GoogleAuthProvider credential. Pass that credential to FirebaseAuth.signInWithCredential so Firebase owns the resulting session.
A Google account selection alone does not sign the user into Firebase.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
Sign out every participating provider
FirebaseAuth.signOut ends the Firebase session, while GoogleSignIn.signOut can clear the provider account selection when desired. Read currentUser only for immediate profile display and let authStateChanges control navigation.
Popping the home route without signing out leaves the SDK session active.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
Future<UserCredential> signInWithGoogle() async {
final google = GoogleSignIn.instance;
await google.initialize();
final user = await google.authenticate();
final auth = user.authentication;
final credential = GoogleAuthProvider.credential(idToken: auth.idToken);
return FirebaseAuth.instance.signInWithCredential(credential);
}
Stream<User?> get authChanges => FirebaseAuth.instance.authStateChanges();
Provider linking and recent-login recovery are explored in a LinkedIn piece about durable account journeys.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowTurn FirebaseAuthException codes into copy
Switch on codes such as invalid-credential, email-already-in-use, weak-password, user-disabled, and too-many-requests. Keep a safe fallback for new codes and log the code rather than the password or token.
Matching English exception.message text is brittle across SDK releases.
String authMessage(FirebaseAuthException error) => switch (error.code) {
'invalid-credential' => 'Check your email and password.',
'email-already-in-use' => 'An account already uses that email.',
'weak-password' => 'Choose a stronger password.',
'too-many-requests' => 'Please wait before trying again.',
_ => 'Sign-in failed. Please try again.',
};
Let the SDK restore sessions
Firebase persists supported sessions and refreshes ID tokens, so most apps should not store passwords or Firebase tokens manually. Listen to idTokenChanges when claims or token refresh events matter beyond sign-in state.
Forcing a login call on every startup adds latency and can trigger rate limits.
class AuthGate extends StatelessWidget {
const AuthGate({super.key});
@override
Widget build(BuildContext context) => StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
return snapshot.data == null
? const Scaffold(body: Center(child: Text('Sign in')))
: const Scaffold(body: Center(child: Text('Home')));
},
);
}
Use the Firebase Authentication emulator when testing account creation, disabled users, and repeated sign-in failures. It lets the AuthGate receive genuine authStateChanges events without creating disposable production accounts. After each test, wait for the stream transition rather than asserting currentUser immediately; the stream is the contract that drives this tutorial's navigation.
Harden each sign-in path before release
Enable every intended provider in the Firebase console before debugging Flutter code, and configure the corresponding platform application. Google sign-in on Android depends on the package name and signing-certificate fingerprints for the build being tested; a release key is different from the debug key. Apple configuration must match the service, team, key, and bundle identifiers. Email/password projects should also decide whether email verification is required before protected data becomes available.
Handle FirebaseAuthException by code, but do not promise that every platform or project setting returns the same level of detail. Useful cases include email-already-in-use, weak-password, too-many-requests, operation-not-allowed, and network-request-failed. Projects that protect against email enumeration can intentionally return a less specific credential error. Keep the precise code in non-sensitive diagnostics while the interface explains the next safe action without revealing whether another person owns an address.
Provider collisions need an account-linking policy rather than a second user record. If the same verified address arrives through another provider, ask the user to authenticate with the existing method, obtain the pending credential, and link it to the current account. Never link solely because two unverified strings match. Reauthentication is also required for sensitive operations such as deleting an account or changing a password after the session becomes old; catch requires-recent-login and resume the original action only after fresh proof.
Test authentication with the Local Emulator Suite or dedicated non-production accounts. Cover cancelled provider UI, wrong credentials, disabled users, offline launch, token refresh, sign-out, account deletion, and a cold start with an existing session. The auth-state stream can emit while dependent profile data is still loading, so model those states separately instead of flashing the home screen. On sign-out, cancel authorised listeners and clear account-scoped caches as well as calling Firebase Auth. Log the provider, operation, result code, and correlation context; never record passwords, ID tokens, refresh tokens, or complete OAuth responses.
Email flows have state outside the immediate form. Password-reset and verification links must use an authorised continue URL, open the intended application or web route, and tolerate an expired or already-used action code. Test those links from a different device and with the app terminated. If the UI optimistically claims verification, reload the current user before trusting cached profile state. Authentication proves identity, not permission: Firestore, Storage, callable functions, and the project’s own API still need server-enforced access rules. A disabled or deleted user may retain a locally cached session until refresh, so protected requests must handle rejection cleanly and return to the sign-in boundary.
Multi-factor authentication, if enabled for the project and audience, adds enrolment, challenge, recovery, and session-management states that do not fit into a simple signed-in boolean. Model them explicitly and test losing access to the enrolled factor. Account deletion also needs a product-level data policy: removing the Auth user does not automatically erase every document, object, or external subscription owned by that identity. A trusted backend workflow should perform the authorised cleanup and record partial failures for recovery.
Common mistakes
- Model authentication with User and a stream: In Firebase authentication, checking currentUser once in main misses later changes and can race SDK restoration; inspect this Firebase authentication cause before changing another Firebase authentication widget.
- Create and sign in email users: In Firebase authentication, displaying raw FirebaseAuthException messages produces inconsistent and overly technical copy; inspect this Firebase authentication cause before changing another Firebase authentication widget.
- Build an AuthGate with StreamBuilder: In Firebase authentication, starting a new authStateChanges listener inside nested pages duplicates routing decisions; inspect this Firebase authentication cause before changing another Firebase authentication widget.
- Exchange Google identity for Firebase credentials: In Firebase authentication, a Google account selection alone does not sign the user into Firebase; inspect this Firebase authentication cause before changing another Firebase authentication widget.
- Sign out every participating provider: In Firebase authentication, popping the home route without signing out leaves the SDK session active; inspect this Firebase authentication cause before changing another Firebase authentication widget.
- Turn FirebaseAuthException codes into copy: In Firebase authentication, matching English exception.message text is brittle across SDK releases; inspect this Firebase authentication cause before changing another Firebase authentication widget.
Frequently asked questions
How does model authentication with User and a stream work in Firebase authentication?
For Firebase authentication, the starting rule is that firebaseAuth.instance owns the current session, currentUser is a snapshot, and authStateChanges emits sign-in and sign-out transitions. Apply this Firebase authentication rule first because model authentication with User and a stream determines whether the Firebase authentication pattern fits.
Why does build an AuthGate with StreamBuilder matter for Firebase authentication?
In Firebase authentication, place MaterialApp above the gate so both branches share navigation, theme, and localisation. Keeping build an AuthGate with StreamBuilder at the Firebase authentication call site exposes the Firebase authentication return value directly.
What failure should I test first in Firebase authentication?
First reproduce the Firebase authentication case where popping the home route without signing out leaves the SDK session active. The verified sign-out path closes provider sessions, cancels authorised listeners, and leaves the auth-state stream in its signed-out state.
How can I verify Firebase authentication before release?
Exercise let the SDK restore sessions with real Firebase authentication inputs on every shipped platform. Inspect the final Firebase authentication callback or output; a successful Firebase authentication button tap alone is not proof.
Further reads
Connect Firebase authentication to the surrounding Flutter stack through these published tutorials:
- Flutter Development Guide 2026
- Flutter Firebase Setup: Connect Android, iOS, and Web
- Flutter Cloud Firestore: CRUD With Real-Time Updates
- Flutter secure_storage: Store Tokens and Secrets Safely
- Flutter Supabase Tutorial: Auth and Database Without Firebase
Sources: FlutterFire and Firebase documentation; Firebase authentication examples verified against current stable Flutter.