Irbisa · Cheatsheet

Firebase & Backend Services

FlutterFire setup, Auth, Firestore, FCM, Remote Config, Crashlytics, App Check, Supabase

Middle Developer · 24 questions

  1. 01

    How do you add Firebase to a Flutter app, and what must main() await before runApp?

    Run flutterfire configure to register the apps and generate firebase_options.dart, add firebase_core plus one plugin per product, and in main() await only Firebase.initializeApp() before runApp.

    // Once per machine:  dart pub global activate flutterfire_cli// Once per project:  flutterfire configureimport 'package:firebase_core/firebase_core.dart';import 'firebase_options.dart'; Future<void> main() async {…
  2. 02

    How do you read and write Firestore data in Flutter, and what is the difference between get() and snapshots()?

    get() reads once and returns a Future; snapshots() returns a Stream that emits now and again on every change until you cancel it.

    final notes = FirebaseFirestore.instance    .collection('notes')    .withConverter<Note>(      fromFirestore: (snap, _) => Note.fromJson(snap.data()!),      toFirestore: (note, _) => note.toJson(),    );…
  3. 03

    What is the difference between Cloud Firestore and Realtime Database, and when would you pick each?

    Firestore stores documents in collections, supports indexed compound queries and bills per document operation; Realtime Database is one JSON tree with very low latency and shallow queries, billed by storage and bandwidth.

  4. 04

    How do you implement Google Sign-In with Firebase Auth, and why does it often work in debug but fail in the Play Store build?

    The google_sign_in package gets a Google ID token, GoogleAuthProvider.credential wraps it, and signInWithCredential exchanges it for a Firebase user.

    Future<void> initGoogle() => GoogleSignIn.instance.initialize(); Future<UserCredential?> signInWithGoogle() async {  if (kIsWeb) {    return FirebaseAuth.instance.signInWithPopup(GoogleAuthProvider());  }…
  5. 05

    At cold start the app shows the login screen for a frame and then jumps to the feed. What is authStateChanges() actually doing?

    authStateChanges() emits its first User? only after the SDK has restored the saved session from disk.

    class AuthGate extends StatelessWidget {  const AuthGate({super.key});   @override  Widget build(BuildContext context) {    return StreamBuilder<User?>(…
  6. 06

    Your staging build writes to the production Firestore even though you swapped firebase_options.dart. What is still pointing at prod?

    flutterfire configure writes three config files, not one: lib/firebase_options.dart plus the native google-services.json (Android) and GoogleService-Info.plist (iOS).

    # One Firebase project per environment, one configure run per flavor.# repeat per build configuration (Release-staging, Profile-staging, ...)flutterfire configure --project=myapp-staging \  --out=lib/firebase/options_staging.dart \  --android-package-name=com.myapp.staging \  --android-out=android/app/src/staging/google-services.json \…
  7. 07

    A guest played for a week, then signed in with Google, and everything they had saved is gone. What did that sign-in do?

    signInWithCredential signed the user into a different account.

    Future<void> startAsGuest() async {  if (FirebaseAuth.instance.currentUser == null) {    await FirebaseAuth.instance.signInAnonymously();  }}…
  8. 08

    The feed must show the three newest comments under every post. How does that requirement change your Firestore model?

    Firestore has no joins, so a screen that must render in one round trip needs a document shaped for that screen: copy the three newest comments onto the post document itself.

  9. 09

    A message you just sent shows up in the list instantly, then jumps to a different position a second later. What are the snapshots telling you?

    You are seeing two snapshot events for one write: first the local one, with hasPendingWrites true and createdAt still null, then the server one with the real timestamp.

  10. 10

    In airplane mode your await docRef.set(...) never returns, yet the new row is already on screen. Is that a bug?

    No. The Future from set() completes only when the server acknowledges the write, while the local cache applied it immediately; the real bug is awaiting a Firestore write in the UI.

    // wrong: offline this Future never completes and the spinner never stopsFuture<void> badSave(String text) async {  setState(() => _saving = true);  await db.collection('notes').add({'text': text});  setState(() => _saving = false);}…
  11. 11

    Two users tap Buy on the last item at the same moment and both get an order. What is wrong with this code?

    Future<void> buy(String itemId, String uid) async {  final item = db.doc('items/$itemId');  final snap = await item.get();  final stock = snap.get('stock') as int;  if (stock <= 0) throw const SoldOut();   await item.update({'stock': stock - 1});  await db.collection('orders').add({'itemId': itemId, 'uid': uid});}

    It is a read-modify-write race: both clients read stock: 1, both pass the check, both write 0, and two orders exist for one item.

  12. 12

    This query works against the emulator, but in production the stream fails with failed-precondition. Why, and how do you fix it for good?

    Stream<List<Order>> openOrders(String uid) => db    .collection('orders')    .where('userId', isEqualTo: uid)    .where('status', isEqualTo: 'open')    .orderBy('createdAt', descending: true)    .limit(20)    .snapshots()    .map((s) => s.docs.map(Order.fromDoc).toList());

    The query needs a composite index on userId, status and createdAt.

  13. 13

    How do Firestore security rules work, and why can't hiding the Delete button from non-authors replace them?

    Rules run on Google's servers for every request and decide it from request.auth, the stored document (resource) and the incoming one (request.resource).

    rules_version = '2';service cloud.firestore {  match /databases/{database}/documents {    match /notes/{noteId} {      allow read, delete: if request.auth != null                          && resource.data.ownerId == request.auth.uid;…
  14. 14

    Push notifications show up when the app is closed, but nothing appears while the user has it open. How does firebase_messaging handle each app state?

    A message with a notification payload is drawn by the operating system, and the OS deliberately does not draw it while your app is in the foreground; it arrives on FirebaseMessaging.onMessage as data for you to show yourself.

    @pragma('vm:entry-point') // release builds tree-shake it otherwiseFuture<void> _onBackground(RemoteMessage message) async {  // A separate isolate: no providers, no navigator, no app state.  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);}…
  15. 15

    Find the bug: after some navigation, one foreground push shows the same banner two or three times.

    class _InboxScreenState extends State<InboxScreen> {  int _unread = 0;   @override  void initState() {    super.initState();    FirebaseMessaging.onMessage.listen((message) {      showLocalBanner(message.notification?.title);      setState(() => _unread++);    });  }   @override  Widget build(BuildContext context) => Text('Unread: $_unread');}

    The onMessage subscription is never cancelled.

  16. 16

    Crashlytics is in the app, users report crashes every day, and the dashboard is empty. What is missing from the wiring?

    Adding the plugin catches native crashes only.

    Future<void> main() async {  WidgetsFlutterBinding.ensureInitialized();  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);  final crashlytics = FirebaseCrashlytics.instance;  await crashlytics.setCrashlyticsCollectionEnabled(!kDebugMode);…
  17. 17

    What do you log with Firebase Analytics in a Flutter app, and what must never go into it?

    Log the events that answer product questions (funnel steps, feature use, purchases) with a few typed parameters, track screens explicitly, and never send personal data: no emails, names, phone numbers or free text.

  18. 18

    The splash screen hangs for eight seconds on a weak connection because startup awaits Remote Config. How should that be wired instead?

    Never await a network fetch before the first frame.

    final rc = FirebaseRemoteConfig.instance; Future<void> bootstrapConfig() async {  await rc.setConfigSettings(RemoteConfigSettings(    fetchTimeout: const Duration(seconds: 8), // default 60 s    minimumFetchInterval:…
  19. 19

    Your callable Cloud Function works against the emulator but returns internal with no message in production. What is going on?

    A callable forwards only an HttpsError (its code, message and details) to the client.

  20. 20

    The Firestore bill jumped to five figures in a month while the user count stayed flat. Where do you look in the Flutter code first?

    Firestore bills per document read, so look for code that reads far more documents than the screen shows: unbounded queries, a listener per list row, filtering or counting in Dart, and listeners that are leaked or re-created.

    // wrong: a new stream on every rebuild re-attaches the listener@overrideWidget build(BuildContext context) => StreamBuilder<QuerySnapshot>(      stream: db.collection('orders').where('userId', isEqualTo: uid).snapshots(),      builder: _rows,    );…
  21. 21

    Find the bug in this OrdersRepository: watchMyOrders() is meant to follow whoever is signed in right now.

    class OrdersRepository {  OrdersRepository(this._auth, this._db);  final FirebaseAuth _auth;  final FirebaseFirestore _db;   // Orders of whoever is signed in right now.  Stream<List<Order>> watchMyOrders() =>      _auth.authStateChanges().asyncExpand((user) {        if (user == null) return Stream.value(const <Order>[]);        return _db            .collection('orders')            .where('userId', isEqualTo: user.uid)            .snapshots()            .map((s) => s.docs.map(Order.fromDoc).toList());      });}

    asyncExpand does not move to the next auth event until the inner stream finishes, and a snapshots() stream never finishes.

  22. 22

    Anyone can pull the API key out of your APK and call Firestore directly. What does App Check add, and what does it still not protect?

    App Check attests that a request came from your genuine, unmodified app on a real device.

    Future<void> main() async {  WidgetsFlutterBinding.ensureInitialized();  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);   // Before the first Firestore, Storage or Functions call.  await FirebaseAppCheck.instance.activate(…
  23. 23

    How do you test a Firebase-backed Flutter app so that no test run touches production and no widget test needs a network?

    Three layers: a repository interface so widget tests never see Firebase at all, the Emulator Suite for code that really talks to Firestore, and the JavaScript rules-testing library for the rules themselves.

    // 1. Widget tests: the seam is a repository, Firebase never loads.class FakeOrders implements OrderRepository {  FakeOrders(this.orders);  final List<Order> orders;  @override  Stream<List<Order>> watch(String uid) => Stream.value(orders);…
  24. 24

    A client asks whether to build on Firebase, on Supabase, or on your own backend. How do you decide, and how do you keep the door open?

    Firestore for realtime, offline-first apps with per-screen reads; Supabase when the data is relational and you need SQL; your own backend when residency, an existing server team or custom logic decides, and keep the vendor SDK out of your widgets, because that is the whole exit path.