Cloud Firestore
Notice
This page is archived and might not reflect the latest version of the FlutterFire plugins. You can find the latest information on firebase.google.com:
- https://firebase.google.com/docs/firestore/quickstart
- https://firebase.google.com/docs/firestore/manage-data/add-data
- https://firebase.google.com/docs/firestore/manage-data/transactions
- https://firebase.google.com/docs/firestore/query-data/get-data
- https://firebase.google.com/docs/firestore/query-data/listen
- And throughout https://firebase.google.com/docs/firestore/... Most pages that have code snippets have Flutter examples. If you find a page that's missing Flutter snippets, please file a bug.
To start using the Cloud Firestore package within your project, import it at the top of your project files:
Before using Firestore, you must first have ensured you have initialized FlutterFire.
To create a new Firestore instance, call the instance
getter on FirebaseFirestore
:
By default, this allows you to interact with Firestore using the default Firebase App used whilst installing FlutterFire on your
platform. If however you'd like to use Firestore with a secondary Firebase App, use the instanceFor
method:
#
Collections & DocumentsFirestore stores data within "documents", which are contained within "collections". Documents can also contain
nested collections. For example, our users would each have their own "document" stored inside the "Users" collection.
The collection
method allows us to reference a collection within our code.
In the below example, we can reference the collection users
, and create a new user document when a button is pressed:
#
Read DataCloud Firestore gives you the ability to read the value of a collection or a document. This can be a one-time read, or provided by realtime updates when the data within a query changes.
#
One-time ReadTo read a collection or document once, call the Query.get
or DocumentReference.get
methods.
In the below example a FutureBuilder
is used to help manage the state
of the request:
To learn more about reading data whilst offline, view the Access Data Offline documentation.
#
Realtime changesFlutterFire provides support for dealing with realtime changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted or added).
Both the CollectionReference
& DocumentReference
provide
a snapshots()
method which returns a Stream
:
Once returned, you can subscribe to updates via the listen()
method. The below example uses a StreamBuilder
which helps automatically manage the streams state and disposal of the stream when it's no longer used within your app:
By default, listeners do not update if there is a change that only affects the metadata. If you want to receive events
when the document or query metadata changes, you can pass includeMetadataChanges
to the snapshots
method:
#
Document & Query SnapshotsWhen performing a query, Firestore returns either a QuerySnapshot
or a DocumentSnapshot
.
#
QuerySnapshotA QuerySnapshot
is returned from a collection query, and allows you to inspect the collection, such as how many documents
exist within it, gives access to the documents within the collection, see any changes since the last query and more.
To access the documents within a QuerySnapshot
, call the docs
property,
which returns a List
containing DocumentSnapshot
classes.
#
DocumentSnapshotA DocumentSnapshot
is returned from a query, or by accessing the
document directly. Even if no document exists in the database, a snapshot will always be returned.
To determine whether the document exists, use the exists
property:
If the document exists, you can read the data of it by calling the data
method, which returns a Map<String, dynamic>
, or null
if it does not exist:
A DocumentSnapshot
also provides the ability to access
deeply nested data without manually iterating the returned Map
via the get
method. The method accepts a dot-separated path or a FieldPath
instance.
If no data exists at the nested path, a StateError
:
#
QueryingCloud Firestore offers advanced capabilities for querying collections. Queries work with both one-time reads or subscribing to changes.
#
FilteringTo filter documents within a collection, the where
method can be chained
onto a collection reference. Filtering supports equality checks and "in" queries. For example, to filter
users where their age is greater than 20:
Firestore also supports array queries. For example, to filter users who speak English (en) or Italian (it), use
the arrayContainsAny
filter:
To learn more about all of the querying capabilities Cloud Firestore has to offer, view the Firebase documentation.
#
LimitingTo limit the number of documents returned from a query, use the limit
method on a collection reference:
You can also limit to the last documents within the collection query by using limitToLast
:
#
OrderingTo order the documents by a specific value, use the orderBy
method:
#
Start & End CursorsTo start and/or end a query at a specific point within a collection, you can pass a value to the startAt
, endAt
,
startAfter
or endBefore
methods. You must specify an order to use cursor queries, for example:
You can further specify a DocumentSnapshot
instead of a specific value,
by passing it to the startAfterDocument
, startAtDocument
, endAtDocument
or endBeforeDocument
methods. For example:
#
Query LimitationsCloud Firestore does not support the following types of queries:
- Queries with range filters on different fields, as described in the previous section.
- Logical OR queries. In this case, you should create a separate query for each OR condition and merge the query results in your app.
- Queries with a != clause. In this case, you should split the query into a greater-than query and a less-than query.
For example, the query clause
where("age", isNotEqualTo: 30)
is not supported, however you can get the same result set by combining two queries, one with the clausewhere("age", isLessThan: 30)
and one with the clausewhere("age", isGreaterThan: 30)
#
Writing DataThe Firebase Documentation provides some great examples on the best practices to structuring your data. It is recommended that you read the guide before building your database.
For more information on what is possible when writing data to Firestore, please refer to this documentation
#
Typing CollectionReference and DocumentReferenceBy default, Firestore references manipulate a Map<String, dynamic>
object. The downside is that we lose type safety.
One solution is to use withConverter
, which will modify methods like CollectionReference.add
or Query.where
to be type-safe.
A common usage of withConverter
is when combined with a serializable class, such as:
We can then use withConverter
to manipulate a collection of movies like so:
Finally, we can use our new moviesRef
variable to perform read and write operations:
#
Adding DocumentsTo add a new document to a collection, use the add
method
on a CollectionReference
:
The add
method adds the new document to your collection with a
unique auto-generated ID. If you'd like to specify your own ID, call the set
method on a DocumentReference
instead:
Calling set
with an id that already exists on the collection will replace all the document data. You can also specify SetOptions(merge: true))
on the query, and this will merge the existing document with the data passed into the set()
:
#
Updating documentsSometimes you may wish to update a document, rather than replacing all of the data. The set
method above replaces any existing data on a given DocumentReference
.
If you'd like to update a document instead, use the update
method:
The method also provides support for updating deeply nested values via dot-notation:
#
Field valuesCloud Firestore supports storing and manipulating values on your database, such as Timestamps, GeoPoints, Blobs and array management.
To store GeoPoint
values, provide the latitude
and longitude to the GeoPoint class:
To store a Blob such as an image, provide a Uint8List
. The below example shows how to get an image from your assets
directory and nest it in the info
object in Firestore.
#
Removing DataTo delete documents with Cloud Firestore, you can use the delete
method on a DocumentReference
:
If you need to remove specific properties from within a document rather than the document itself,
you can use the delete
method with
the FieldValue
class:
#
TransactionsTransactions are a way to ensure that a write operation only occurs using the latest data available on the server. Transactions never partially apply writes, and writes execute at the end of a successful transaction.
Transactions are useful when you want to update a field based on its current value, or the value of another field. If you want to write multiple documents without using the documents current state, a batch write should be used.
When using transactions, note that:
- Read operations must come before write operations
- Transactions will fail when the client is offline, they cannot use cached data
An example of where a transaction could be used would be in an application where a user can subscribe to a channel. When a user presses the subscribe button, a "subscribers" field in a document increments. Without using Transactions, we would first need to read the existing value, and then increment that value using two separate operations.
On a high traffic application, the value on the server could have already changed by the time the write operation sets a new value, causing the number to be inconsistent.
Transactions remove this issue by atomically updating the value of the server. If the value changes whilst the transaction is executing, it will retry, ensuring the value on the server is used, rather than the client value.
To execute a transaction, call the runTransaction
method:
In the above example, if the document changes at any point during the transaction, it will retry up-to five times.
You should not directly modify application state inside of the transaction, as the handler may execute multiple times. You should instead return a value at the end of the handler, updating application state once the transaction has completed.
If an exception is thrown within the handler, the entire transaction will be aborted.
#
Batch writeFirestore lets you execute multiple write operations as a single batch that can contain any
combination of set
, update
,
or delete
operations.
First, create a new batch instance via the batch
method, then perform
the operations on the batch, and then commit it once ready. The below example shows how to delete
all documents in a collection in a single operation:
#
Data SecurityIt is important that you understand how to write rules in your Firebase console to ensure that your data is secure. Please follow the Firebase Firestore documentation on security.
#
Access Data Offline#
Configure Offline PersistenceFirestore provides out of the box support for offline capabilities. When reading and writing data, Firestore uses a local database which automatically synchronizes with the server. Cloud Firestore functionality continues when users are offline, and automatically handles data migration when they regain connectivity.
This functionality is enabled by default, however it can be disabled if needed. The settings
must be set before any Firestore interaction is performed:
If you want to clear any persisted data, you can call the clearPersistence()
method.
Calls to update settings or clearing persistence must be carried out before any other usage of Firestore. If called afterwards, they will take effect on the next Firestore claim (e.g. restarting the application).
#
Configure Cache SizeWhen persistence is enabled, Firestore caches every document for offline access. After exceeding the cache size, Firestore will attempt to remove older, unused data. You can configure different cache sizes, or disable the removal process:
#
Disable and Enable Network AccessIt is possible to disable network access for your Firestore client. While network access is disabled, all Firestore requests retrieve results from the cache. Any write operations are queued until network access is re-enabled.
To re-enabled network access, call the enableNetwork
method:
#
Emulator UsageIf you are using the local Firestore emulators, then it is possible to
connect to this using the useFirestoreEmulator
method. Ensure you pass the correct port on which the Firebase emulator is running on.
Ensure you have enabled network connections to the emulators in your apps following the emulator usage instructions in the general FlutterFire installation notes for each operating system.
#
Data BundlesIf you have setup a data bundle and are currently serving it, you may load data bundles into your app.
#
Named QueryIf you have loaded a bundle with a named query, you may retrieve the
query snapshot by calling the namedQueryGet()
API:
#
Distributed CountersTo support more frequent counter updates, create a distributed counter. Each counter is a document with a subcollection of shards
,
and the value of the counter is the sum of the value of the shards
.
Write throughput increases linearly with the number of shards
, so a distributed counter with 10 shards
can handle 10x as many writes as a traditional counter.
note
An alternative solution is to use Firebase extensions, please refer to the Ditributed Counters extension and how to set it up here.
A distributed counter collection would look like this:
The following code initializes a distributed counter:
To increment the counter, choose a random shard and increment the count
:
To get the total count, query for all shards and sum their count
fields:
Refer to the official documentation on distributed counters for more details.