Lesson 47 of 51 intermediate

Firebase Setup & Auth

Adding Firebase to Your Flutter App

Open interactive version (quiz + challenge)

Real-world analogy

Firebase is like renting a fully furnished office instead of building one from scratch. Need user accounts? Firebase Auth is the reception desk. Need crash reports? Crashlytics is the security camera. Need to change settings without redeploying? Remote Config is the intercom system. Google built and maintains the whole building -- you just move in and start working!

What is it?

Firebase is Google's suite of cloud services for mobile apps. Firebase Auth handles user authentication (email, Google, Apple sign-in). Crashlytics captures and reports crashes. In team_mvp_kit, Firebase Auth is used for social logins -- the Firebase ID token is exchanged with the backend for app-specific access and refresh tokens.

Real-world relevance

team_mvp_kit uses Firebase as a bridge for authentication. Users sign in with Google or Apple through Firebase Auth, then the app sends the Firebase ID token to the backend server. The backend verifies it and returns its own JWT tokens. Crashlytics catches every crash in production so the team can fix issues before users even report them.

Key points

Code example

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'firebase_options.dart';

// 1. App initialization with Firebase
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Firebase
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  // Setup Crashlytics error handlers
  FlutterError.onError =
      FirebaseCrashlytics.instance.recordFlutterError;

  PlatformDispatcher.instance.onError = (error, stack) {
    FirebaseCrashlytics.instance.recordError(error, stack);
    return true;
  };

  await configureDependencies();
  runApp(const MyApp());
}

// 2. FirebaseAuthService wraps Firebase Auth (team_mvp_kit)
@lazySingleton
class FirebaseAuthService {
  final FirebaseAuth _auth;
  FirebaseAuthService(this._auth);

  // Current user
  User? get currentUser => _auth.currentUser;
  bool get isSignedIn => currentUser != null;

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

  // Email/password sign up
  Future<UserCredential> signUpWithEmail({
    required String email,
    required String password,
  }) async {
    return _auth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

  // Email/password sign in
  Future<UserCredential> signInWithEmail({
    required String email,
    required String password,
  }) async {
    return _auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
  }

  // Get ID token for backend exchange
  Future<String?> getIdToken() async {
    return currentUser?.getIdToken();
  }

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

// 3. Module to register FirebaseAuth instance
@module
abstract class FirebaseModule {
  @singleton
  FirebaseAuth get firebaseAuth => FirebaseAuth.instance;

  @singleton
  FirebaseCrashlytics get crashlytics =>
      FirebaseCrashlytics.instance;
}

Line-by-line walkthrough

  1. 1. Import firebase_core for initialization
  2. 2. Import firebase_auth for authentication
  3. 3. Import firebase_crashlytics for crash reporting
  4. 4. Import the generated Firebase config for this project
  5. 5.
  6. 6. Comment: App initialization with all Firebase services
  7. 7. Main function marked as async for await calls
  8. 8. Ensure Flutter is ready before async operations
  9. 9.
  10. 10. Comment: Initialize Firebase with platform-specific config
  11. 11. Call Firebase.initializeApp with the generated options
  12. 12. DefaultFirebaseOptions.currentPlatform picks Android/iOS/web config
  13. 13. Closing the Firebase.initializeApp call
  14. 14.
  15. 15. Comment: Set up Crashlytics to catch all errors
  16. 16. Assign Flutter framework errors to Crashlytics recorder
  17. 17. Closing the FlutterError.onError assignment
  18. 18.
  19. 19. Set up platform error handler for async Dart errors
  20. 20. Record the error and stack trace to Crashlytics
  21. 21. Return true to mark the error as handled
  22. 22. Closing the PlatformDispatcher setup
  23. 23.
  24. 24. Initialize dependency injection (after Firebase is ready)
  25. 25. Start the app
  26. 26. Closing main
  27. 27.
  28. 28. Comment: FirebaseAuthService wraps FirebaseAuth
  29. 29. @lazySingleton for DI registration
  30. 30. Class declaration
  31. 31. Private FirebaseAuth instance (injected)
  32. 32. Constructor receives FirebaseAuth
  33. 33.
  34. 34. Comment: Current user access
  35. 35. Getter returns the currently signed-in user or null
  36. 36. Getter checks if a user is signed in
  37. 37.
  38. 38. Comment: Auth state stream for reactive listening
  39. 39. Returns the authStateChanges stream from FirebaseAuth
  40. 40. Closing the getter
  41. 41.
  42. 42. Comment: Email/password registration
  43. 43. signUpWithEmail method with required email and password
  44. 44. Calls FirebaseAuth createUserWithEmailAndPassword
  45. 45. Passes email and password
  46. 46. Closing the method
  47. 47.
  48. 48. Comment: Email/password sign in
  49. 49. signInWithEmail method with required email and password
  50. 50. Calls FirebaseAuth signInWithEmailAndPassword
  51. 51. Passes email and password
  52. 52. Closing the method
  53. 53.
  54. 54. Comment: Get Firebase ID token for backend exchange
  55. 55. getIdToken returns the current user's ID token
  56. 56. Calls getIdToken on the current user (null-safe)
  57. 57. Closing the method
  58. 58.
  59. 59. Comment: Sign out
  60. 60. signOut method clears the Firebase session
  61. 61. Calls signOut on FirebaseAuth instance
  62. 62. Closing signOut and FirebaseAuthService
  63. 63.
  64. 64. Comment: Module to provide Firebase instances to DI
  65. 65. @module annotation for third-party dependencies
  66. 66. Abstract class for the module
  67. 67. @singleton provides one FirebaseAuth instance
  68. 68. Returns FirebaseAuth.instance
  69. 69.
  70. 70. @singleton provides one Crashlytics instance
  71. 71. Returns FirebaseCrashlytics.instance
  72. 72. Closing the module

Spot the bug

void main() {
  runApp(const MyApp());
  Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
}
Need a hint?
Think about the order of operations and async requirements...
Show answer
Three bugs: 1) main() is not async and Firebase.initializeApp is not awaited. 2) Firebase.initializeApp is called AFTER runApp -- it must be before. 3) WidgetsFlutterBinding.ensureInitialized() is missing, which is required before any async operations in main(). Fix: add async, add WidgetsFlutterBinding.ensureInitialized(), await Firebase.initializeApp() BEFORE runApp().

Explain like I'm 5

Imagine you are opening a lemonade stand. Instead of building your own cash register, security cameras, and customer list from scratch, you rent them all from a big company called Firebase. Their cash register (Auth) remembers who your customers are. Their security camera (Crashlytics) tells you if anything breaks. And you can change your menu board (Remote Config) without rebuilding the whole stand!

Fun fact

Firebase was originally a real-time chat service called Envolve, founded in 2011. The developers noticed people were using their chat infrastructure to sync app data instead of chatting, so they pivoted to become a backend-as-a-service. Google acquired Firebase in 2014 and it now powers over 3 million apps worldwide!

Hands-on challenge

Set up Firebase in a Flutter project: run flutterfire configure, initialize Firebase in main(), set up Crashlytics error handlers, and create a simple email/password sign-up and sign-in flow. Display the user's UID after signing in.

More resources

Open interactive version (quiz + challenge) ← Back to course: Flutter & Dart