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
- What is Firebase? — Firebase is a suite of cloud services by Google. For Flutter apps, the most useful services are: Authentication (login/signup), Crashlytics (crash reporting), Remote Config (change app behavior without updates), Cloud Firestore (real-time database), and Cloud Messaging (push notifications). team_mvp_kit uses Auth and Crashlytics.
- Adding Firebase to Flutter — Use the FlutterFire CLI to configure Firebase automatically. Run 'flutterfire configure' and it generates the firebase_options.dart file with your project's API keys and configuration for each platform (Android, iOS, web). This replaces the old manual google-services.json setup.
- Firebase Initialization — Initialize Firebase in main() before runApp(). This must happen before any Firebase service is used. In team_mvp_kit, Firebase init is part of the dependency injection setup to ensure correct initialization order.
- Firebase Authentication Basics — FirebaseAuth provides methods for creating accounts, signing in, signing out, and listening to auth state changes. It supports email/password, Google, Apple, phone number, and anonymous sign-in. team_mvp_kit uses Firebase Auth primarily for social logins and backend token exchange.
- Auth State Listener — FirebaseAuth provides an authStateChanges() stream that emits whenever the user signs in or out. Use this to reactively update your UI or routing. When the stream emits null, the user is signed out. When it emits a User, they are signed in.
- Getting Firebase ID Token — After signing in with Firebase, get the ID token to send to your backend server. The backend verifies this token with Firebase Admin SDK to confirm the user's identity. This is how team_mvp_kit bridges Firebase Auth with its own backend API.
- Crashlytics Setup — Firebase Crashlytics automatically captures and reports app crashes. Initialize it after Firebase and enable it in main(). Uncaught Flutter errors and Dart errors are automatically sent. You can also log custom errors and add user context for better debugging.
- Custom Crashlytics Logging — Add custom keys, log messages, and record non-fatal errors to Crashlytics for better debugging. Set the user ID so you can filter crashes by user. team_mvp_kit sets the user ID after login and logs important state changes.
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. Import firebase_core for initialization
- 2. Import firebase_auth for authentication
- 3. Import firebase_crashlytics for crash reporting
- 4. Import the generated Firebase config for this project
- 5.
- 6. Comment: App initialization with all Firebase services
- 7. Main function marked as async for await calls
- 8. Ensure Flutter is ready before async operations
- 9.
- 10. Comment: Initialize Firebase with platform-specific config
- 11. Call Firebase.initializeApp with the generated options
- 12. DefaultFirebaseOptions.currentPlatform picks Android/iOS/web config
- 13. Closing the Firebase.initializeApp call
- 14.
- 15. Comment: Set up Crashlytics to catch all errors
- 16. Assign Flutter framework errors to Crashlytics recorder
- 17. Closing the FlutterError.onError assignment
- 18.
- 19. Set up platform error handler for async Dart errors
- 20. Record the error and stack trace to Crashlytics
- 21. Return true to mark the error as handled
- 22. Closing the PlatformDispatcher setup
- 23.
- 24. Initialize dependency injection (after Firebase is ready)
- 25. Start the app
- 26. Closing main
- 27.
- 28. Comment: FirebaseAuthService wraps FirebaseAuth
- 29. @lazySingleton for DI registration
- 30. Class declaration
- 31. Private FirebaseAuth instance (injected)
- 32. Constructor receives FirebaseAuth
- 33.
- 34. Comment: Current user access
- 35. Getter returns the currently signed-in user or null
- 36. Getter checks if a user is signed in
- 37.
- 38. Comment: Auth state stream for reactive listening
- 39. Returns the authStateChanges stream from FirebaseAuth
- 40. Closing the getter
- 41.
- 42. Comment: Email/password registration
- 43. signUpWithEmail method with required email and password
- 44. Calls FirebaseAuth createUserWithEmailAndPassword
- 45. Passes email and password
- 46. Closing the method
- 47.
- 48. Comment: Email/password sign in
- 49. signInWithEmail method with required email and password
- 50. Calls FirebaseAuth signInWithEmailAndPassword
- 51. Passes email and password
- 52. Closing the method
- 53.
- 54. Comment: Get Firebase ID token for backend exchange
- 55. getIdToken returns the current user's ID token
- 56. Calls getIdToken on the current user (null-safe)
- 57. Closing the method
- 58.
- 59. Comment: Sign out
- 60. signOut method clears the Firebase session
- 61. Calls signOut on FirebaseAuth instance
- 62. Closing signOut and FirebaseAuthService
- 63.
- 64. Comment: Module to provide Firebase instances to DI
- 65. @module annotation for third-party dependencies
- 66. Abstract class for the module
- 67. @singleton provides one FirebaseAuth instance
- 68. Returns FirebaseAuth.instance
- 69.
- 70. @singleton provides one Crashlytics instance
- 71. Returns FirebaseCrashlytics.instance
- 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
- FlutterFire Overview (FlutterFire)
- Firebase Auth for Flutter (FlutterFire)
- Firebase Crashlytics for Flutter (FlutterFire)