Flutter Cloud Firestore: CRUD With Real-Time Updates

Coding Liquids blog cover featuring Sagnik Bhattacharya for Flutter Cloud Firestore: CRUD With Real-Time Updates, with collection and document references, add, set, update, and delete, and snapshots with streambuilder.
Coding Liquids blog cover featuring Sagnik Bhattacharya for Flutter Cloud Firestore: CRUD With Real-Time Updates, with collection and document references, add, set, update, and delete, and snapshots with streambuilder.

A live demonstration shows remote document edits arriving without a manual refresh.

Subscribe on YouTube@codingliquids

Shape data as collections and documents

A collection groups documents, each document has an identifier, and fields hold supported scalar, map, list, timestamp, or reference values. Choose paths around access patterns because Firestore is not a relational join engine.

Deeply nested collections and unbounded arrays make rules and updates harder to reason about.

Create merge and update documents

add creates a document with an automatic ID, set writes a chosen document, set with merge preserves unspecified fields, and update requires existence. Use server timestamps for shared update ordering rather than trusting device clocks.

Calling update for a missing document fails, while plain set can overwrite fields unintentionally.

flutter pub add cloud_firestore

The Instagram Firestore sketches contrast one-shot reads, listeners, pending writes, and deterministic pagination.

Follow me on Instagram@sagnikteaches

Choose one-time get or live snapshots

get performs one server or cache-backed read, whereas snapshots returns a stream of query changes. Use get for a single decision and snapshots for UI that must react to remote edits.

Leaving a live listener active for data that never changes consumes reads and resources.

The Complete Flutter Guide course thumbnail

Build production-ready Cloud Firestore features

The Complete Flutter Guide turns cloud firestore into maintainable app architecture, polished UI, and testable production code.

Enrol now

Add cloud_firestore before creating the typed collection reference used by the CRUD examples.

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: any

Render a live QuerySnapshot list

StreamBuilder> can map snapshot.docs to typed models and render loading, error, empty, and data states. Give list rows stable document IDs as keys when reordering is possible.

Reading doc.data repeatedly with dynamic maps spreads cast failures into the builder.

import 'package:cloud_firestore/cloud_firestore.dart';

Compose indexed Firestore queries

where filters, orderBy controls ordering, and limit bounds result size; compound combinations may require a generated index. Follow the console link from a failed-precondition response to create the exact composite index.

A query that works on a tiny emulator dataset may become expensive without limits in production.

import 'package:cloud_firestore/cloud_firestore.dart';

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

Future<void> createNote(Note note) => notes.add(note);
Future<void> renameNote(String id, String title) =>
    notes.doc(id).update({'title': title});
Future<void> deleteNote(String id) => notes.doc(id).delete();
Stream<QuerySnapshot<Note>> watchNotes() =>
    notes.orderBy('updatedAt', descending: true).limit(50).snapshots();

My LinkedIn post explains why rules, converters, query indexes, and contention belong in one data-design conversation.

Connect on LinkedInSagnik Bhattacharya
The Complete Flutter Guide course thumbnail

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 now

Convert snapshots to Note objects

withConverter attaches fromFirestore and toFirestore functions to a collection reference. Typed DocumentReference and Query objects then carry Note rather than Map.

Using a non-null assertion for fields not enforced by old data can break migrations.

StreamBuilder<QuerySnapshot<Note>>(
  stream: notes.orderBy('updatedAt', descending: true).limit(50).snapshots(),
  builder: (context, snapshot) {
    if (snapshot.hasError) return Text('Could not load notes: ${snapshot.error}');
    if (!snapshot.hasData) return const CircularProgressIndicator();
    final docs = snapshot.data!.docs;
    if (docs.isEmpty) return const Text('No notes yet');
    return ListView(
      children: [for (final doc in docs) ListTile(key: ValueKey(doc.id), title: Text(doc.data().title))],
    );
  },
)

Delete documents under real security rules

doc(id).delete removes that document but does not recursively remove nested subcollections. Rules should require authentication, validate ownership and field shapes, and distinguish create from update.

Client-side hidden buttons are not access control because callers can invoke the API directly.

Model Firestore around contention and rules

Document shape affects both cost and update behaviour. Keep fields that change together in one document, but avoid an ever-growing array or map that turns every small edit into a large contested write. Give documents stable IDs when other records refer to them, use server timestamps for authoritative creation and update times, and represent deletion explicitly if clients must observe tombstones. A converter can keep snapshot parsing out of widgets and define how missing fields from older records are handled.

Security rules are not query filters. A query must be capable of returning only documents the caller may read, so ownership constraints often need to appear in both the rule and the query. Test rules against another user’s document, a signed-out request, forbidden fields, and an attempted ownership change. The client’s Firebase configuration identifies the project but does not grant trust; every write still needs validation of identity, allowed keys, types, and invariants on the backend boundary.

Composite-index failures usually include enough information to create the required index. Preserve the error code and inspect the query’s equality filters, range filter, and ordering before adding indexes indiscriminately. Pagination should carry the last snapshot or explicit ordered field values from the previous page. If the sort value is not unique, add the document ID as a deterministic tie-breaker so equal timestamps cannot move or vanish between page requests.

Use a transaction when a write depends on a current server value, and a batch when several independent writes merely need one atomic commit. Transactions can run more than once under contention, so their callback must not send emails, mutate UI state, or perform other irreversible side effects. Exercise offline writes, permission denial, a missing document, concurrent edits, and listener disposal in tests. Production telemetry should identify the collection, operation, duration, and Firebase code without copying document contents. That evidence makes permission-denied, failed-precondition, and unavailable meaningfully different failures.

Optimistic UI needs a reconciliation rule for snapshots that include local pending writes. Snapshot metadata can distinguish cached or pending state when the design needs to show it, but most screens should still use the stream as their source of truth. Do not generate a new document ID every time a retried save button is pressed; allocate it once for the logical record or use a backend idempotency strategy. Emulator tests can seed deterministic documents, apply rules, and run concurrent updates without touching production. Clear the emulator between cases so a passing query never depends on rows left by another test.

Listener ownership should follow the screen or repository scope exactly. Cancelling it after navigation prevents hidden routes from continuing to receive billable updates and attempting state changes.

Common mistakes

  • Shape data as collections and documents: In Cloud Firestore CRUD, deeply nested collections and unbounded arrays make rules and updates harder to reason about; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.
  • Create merge and update documents: In Cloud Firestore CRUD, calling update for a missing document fails, while plain set can overwrite fields unintentionally; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.
  • Choose one-time get or live snapshots: In Cloud Firestore CRUD, leaving a live listener active for data that never changes consumes reads and resources; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.
  • Render a live QuerySnapshot list: In Cloud Firestore CRUD, reading doc.data repeatedly with dynamic maps spreads cast failures into the builder; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.
  • Compose indexed Firestore queries: In Cloud Firestore CRUD, a query that works on a tiny emulator dataset may become expensive without limits in production; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.
  • Convert snapshots to Note objects: In Cloud Firestore CRUD, using a non-null assertion for fields not enforced by old data can break migrations; inspect this Cloud Firestore CRUD cause before changing another Cloud Firestore CRUD widget.

Frequently asked questions

How does shape data as collections and documents work in Cloud Firestore CRUD?

For Cloud Firestore CRUD, the starting rule is that a collection groups documents, each document has an identifier, and fields hold supported scalar, map, list, timestamp, or reference values. Apply this Cloud Firestore CRUD rule first because shape data as collections and documents determines whether the Cloud Firestore CRUD pattern fits.

Why does choose one-time get or live snapshots matter for Cloud Firestore CRUD?

In Cloud Firestore CRUD, use get for a single decision and snapshots for UI that must react to remote edits. Keeping choose one-time get or live snapshots at the Cloud Firestore CRUD call site exposes the Cloud Firestore CRUD return value directly.

What failure should I test first in Cloud Firestore CRUD?

First reproduce the Cloud Firestore CRUD case where a query that works on a tiny emulator dataset may become expensive without limits in production. A production-shaped retry should use the intended limit and index while returning the same deterministic order as the small fixture.

How can I verify Cloud Firestore CRUD before release?

Exercise delete documents under real security rules with real Cloud Firestore CRUD inputs on every shipped platform. Inspect the final Cloud Firestore CRUD callback or output; a successful Cloud Firestore CRUD button tap alone is not proof.

Further reads

Connect Cloud Firestore CRUD to the surrounding Flutter stack through these published tutorials:

Sources: FlutterFire and Firebase documentation; Cloud Firestore CRUD examples verified against current stable Flutter.