# How to Use Firebase Auth with Flutter

- Tool: Firebase
- Difficulty: Intermediate
- Time required: 20-25 min
- Compatibility: Flutter 3.x+, firebase_core 3.x+, firebase_auth 5.x+, all Firebase plans
- Last updated: March 2026

## TL;DR

To use Firebase Auth with Flutter, add the FlutterFire packages (firebase_core and firebase_auth) to your project and run the FlutterFire CLI to auto-generate platform configuration. Use StreamBuilder with FirebaseAuth.instance.authStateChanges() to reactively rebuild the UI when the user signs in or out. Firebase Auth in Flutter supports email/password, Google Sign-In, Apple Sign-In, phone authentication, and anonymous auth out of the box.

## Implementing Firebase Authentication in Flutter

FlutterFire provides official Firebase plugins for Flutter that work across iOS, Android, web, and desktop. This tutorial walks through configuring Firebase in a Flutter project using the FlutterFire CLI, implementing email/password authentication with sign-up and login screens, reacting to auth state changes with StreamBuilder, and adding Google Sign-In as an OAuth provider. You will build a complete auth flow that persists user sessions and handles errors gracefully.

## Before you start

- Flutter SDK 3.x+ installed with a working development environment
- A Firebase project created in the Firebase Console
- FlutterFire CLI installed (dart pub global activate flutterfire_cli)
- Email/Password sign-in enabled in Firebase Console > Authentication > Sign-in method

## Step-by-step guide

### 1. Configure Firebase with the FlutterFire CLI

The FlutterFire CLI auto-generates platform-specific configuration files so you do not need to manually add google-services.json or GoogleService-Info.plist. Run flutterfire configure in your project root, select your Firebase project, and choose the platforms you want to configure. The CLI creates a firebase_options.dart file with your Firebase config.

```
# Install FlutterFire CLI
dart pub global activate flutterfire_cli

# Run configuration (select your Firebase project)
flutterfire configure

# Add Firebase packages to pubspec.yaml
flutter pub add firebase_core firebase_auth
```

**Expected result:** A lib/firebase_options.dart file is generated with platform-specific configuration for your Firebase project.

### 2. Initialize Firebase in your app

Call Firebase.initializeApp() in your main() function before running the app. Pass the DefaultFirebaseOptions from the generated firebase_options.dart file. This must complete before any Firebase service is used.

```
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}
```

**Expected result:** Firebase is initialized and all Firebase services (Auth, Firestore, etc.) are available throughout the app.

### 3. Implement email/password sign-up and sign-in

Use FirebaseAuth.instance to access auth methods. createUserWithEmailAndPassword() registers new users, and signInWithEmailAndPassword() logs in existing users. Both return a UserCredential containing the User object. Wrap calls in try-catch to handle errors like weak-password, email-already-in-use, and wrong-password.

```
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<String?> signUp(String email, String password) async {
    try {
      await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return null; // success
    } on FirebaseAuthException catch (e) {
      return e.message; // return error message
    }
  }

  Future<String?> signIn(String email, String password) async {
    try {
      await _auth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      return null;
    } on FirebaseAuthException catch (e) {
      return e.message;
    }
  }

  Future<void> signOut() async {
    await _auth.signOut();
  }

  User? get currentUser => _auth.currentUser;
}
```

**Expected result:** Users can create accounts and sign in with email and password, with errors returned as human-readable messages.

### 4. React to auth state changes with StreamBuilder

FirebaseAuth.instance.authStateChanges() returns a Stream that emits the current User whenever the auth state changes (sign in, sign out, token refresh). Use StreamBuilder at the top of your widget tree to switch between authentication screens and the main app based on the current user.

```
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        // Show loading indicator while checking auth state
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Scaffold(
            body: Center(child: CircularProgressIndicator()),
          );
        }

        // User is signed in
        if (snapshot.hasData) {
          return const HomeScreen();
        }

        // User is not signed in
        return const LoginScreen();
      },
    );
  }
}
```

**Expected result:** The app automatically navigates between login and home screens when the user signs in or out.

### 5. Add Google Sign-In

Google Sign-In requires the google_sign_in package for the native flow and GoogleAuthProvider from firebase_auth. The user signs in with Google first (which shows the native account picker), then the Google credential is used to authenticate with Firebase. Enable Google as a sign-in provider in the Firebase Console.

```
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';

Future<String?> signInWithGoogle() async {
  try {
    // Trigger the Google Sign-In flow
    final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
    if (googleUser == null) return 'Sign-in cancelled';

    // Get auth details from the Google account
    final GoogleSignInAuthentication googleAuth =
        await googleUser.authentication;

    // Create a Firebase credential
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    // Sign in to Firebase with the Google credential
    await FirebaseAuth.instance.signInWithCredential(credential);
    return null; // success
  } catch (e) {
    return e.toString();
  }
}

// Add to pubspec.yaml: google_sign_in: ^6.0.0
```

**Expected result:** Users can sign in with their Google account using the native account picker, and the session is managed by Firebase Auth.

### 6. Protect routes and display user info

Once authenticated, access the current user's information through FirebaseAuth.instance.currentUser. Use this to display the user's email, name, or photo, and to protect screens that require authentication. The User object provides uid, email, displayName, photoURL, and emailVerified properties.

```
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final user = FirebaseAuth.instance.currentUser!;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            icon: const Icon(Icons.logout),
            onPressed: () => FirebaseAuth.instance.signOut(),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (user.photoURL != null)
              CircleAvatar(
                backgroundImage: NetworkImage(user.photoURL!),
                radius: 40,
              ),
            const SizedBox(height: 16),
            Text('Welcome, ${user.displayName ?? user.email}'),
            Text('UID: ${user.uid}'),
          ],
        ),
      ),
    );
  }
}
```

**Expected result:** The home screen displays the authenticated user's name, photo, and UID with a sign-out button.

## Complete code example

File: `auth_service.dart`

```dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Auth state stream for StreamBuilder
  Stream<User?> get authStateChanges => _auth.authStateChanges();

  // Current user (may be null)
  User? get currentUser => _auth.currentUser;

  // Email/password sign-up
  Future<String?> signUp(String email, String password) async {
    try {
      await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return null;
    } on FirebaseAuthException catch (e) {
      return _mapErrorCode(e.code);
    }
  }

  // Email/password sign-in
  Future<String?> signIn(String email, String password) async {
    try {
      await _auth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      return null;
    } on FirebaseAuthException catch (e) {
      return _mapErrorCode(e.code);
    }
  }

  // Google Sign-In
  Future<String?> signInWithGoogle() async {
    try {
      final googleUser = await GoogleSignIn().signIn();
      if (googleUser == null) return 'Sign-in cancelled.';

      final googleAuth = await googleUser.authentication;
      final credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );

      await _auth.signInWithCredential(credential);
      return null;
    } catch (e) {
      return e.toString();
    }
  }

  // Sign out
  Future<void> signOut() async {
    await GoogleSignIn().signOut();
    await _auth.signOut();
  }

  // Map error codes to user-friendly messages
  String _mapErrorCode(String code) {
    switch (code) {
      case 'weak-password':
        return 'Password must be at least 6 characters.';
      case 'email-already-in-use':
        return 'An account with this email already exists.';
      case 'invalid-email':
        return 'Please enter a valid email address.';
      case 'user-not-found':
        return 'No account found with this email.';
      case 'wrong-password':
        return 'Incorrect password. Please try again.';
      default:
        return 'An error occurred. Please try again.';
    }
  }
}
```

## Common mistakes

- **Forgetting to call WidgetsFlutterBinding.ensureInitialized() before Firebase.initializeApp() in main()** — undefined Fix: Add WidgetsFlutterBinding.ensureInitialized() as the first line in main() before any async operations.
- **Not running flutterfire configure after changing Firebase project settings or adding platforms** — undefined Fix: Run flutterfire configure again whenever your Firebase project settings change. This regenerates firebase_options.dart.
- **Using currentUser directly in the widget tree instead of authStateChanges() with StreamBuilder** — undefined Fix: currentUser can be null during initialization. Use StreamBuilder with authStateChanges() for reactive, reliable auth state handling.
- **Not adding SHA-1 fingerprint for Android Google Sign-In, causing authentication to fail silently** — undefined Fix: Generate your debug SHA-1 with 'keytool -list -v -keystore ~/.android/debug.keystore' and add it in Firebase Console > Project Settings > Your Apps > Android.

## Best practices

- Use the FlutterFire CLI (flutterfire configure) for automatic platform configuration instead of manual setup
- Create an AuthService class to encapsulate all authentication logic and keep widgets clean
- Use StreamBuilder with authStateChanges() at the top of the widget tree for reactive auth routing
- Map FirebaseAuthException error codes to user-friendly messages instead of showing raw error strings
- Sign out of both Google and Firebase when implementing Google Sign-In sign-out
- Enable only the sign-in providers you actually use in the Firebase Console to reduce attack surface
- Test on both iOS and Android since platform-specific configuration differs (SHA-1, URL schemes)

## Frequently asked questions

### Do I need to manually add google-services.json and GoogleService-Info.plist?

No. The FlutterFire CLI (flutterfire configure) automatically generates and places these files. You only need to run the CLI and select your platforms.

### Does Firebase Auth persist the login session in Flutter?

Yes. Firebase Auth automatically persists the session to secure storage on mobile devices. When the app restarts, the user remains signed in until they explicitly sign out.

### How do I add Apple Sign-In to my Flutter app?

Add the sign_in_with_apple package, enable Apple as a sign-in provider in Firebase Console, configure your Apple Developer account with a Service ID, and use AppleAuthProvider with signInWithProvider().

### What is the difference between authStateChanges and idTokenChanges?

authStateChanges emits when the user signs in or out. idTokenChanges also emits when the ID token is refreshed (e.g., after custom claims change). Use idTokenChanges if you need to react to token updates.

### Can I use Firebase Auth without the FlutterFire CLI?

Yes, but it requires manual setup: downloading google-services.json, modifying build.gradle for Android, adding GoogleService-Info.plist to Xcode, and configuring each platform individually. The CLI automates all of this.

### Can RapidDev help build a Flutter app with Firebase authentication?

Yes. RapidDev can implement complete authentication flows in Flutter including email/password, social login, phone auth, and role-based access control with Firestore custom claims.

---

Source: https://www.rapidevelopers.com/firebase-tutorial/how-to-use-firebase-auth-with-flutter
© RapidDev — https://www.rapidevelopers.com/firebase-tutorial/how-to-use-firebase-auth-with-flutter
