Cloud Firestore Basics in Flutter: How to Get, Add, Edit, and Delete Data in Cloud Firestore, Demonstrated in a
Real Flutter App
Tags: cloudfirestore, flutter, dart, firebase
Published: 2020-08-17 14:50:55

![](https://cdn-images-1.medium.com/max/880/1*32IIQORIjoowKCizb3XTog.png)

In this post, I will be demonstrating how to do the basic database operations in
Cloud Firestore in a Flutter app, using a published app I built as a practical
example.

**You will learn how to implement the core database operations in Cloud
Firestore: how to get (read), add (write), edit, and delete data in the database
in a Flutter app.**

> **Note:** *This article does not cover setting up Cloud Firestore, Firebase
> Authentication/Sign in for Flutter, and documents/collections in Cloud
Firestore, which are prerequisites for this tutorial. *If you are unfamiliar
with these topics,* *I highly encourage you to [read the Cloud Firestore
plugin’s Readme](https://pub.dev/packages/cloud_firestore) and [check out this
code lab to setup Firebase with
Flutter](https://codelabs.developers.google.com/codelabs/flutter-firebase/index.html?index=..%2F..index#6),
check out the [_Reply project as a
guide](https://github.com/ashtonjonesdev/reply_flutter), and [watch this video
to learn about documents and collections in Cloud
Firestore](https://www.youtube.com/watch?v=v_hR4K4auoQ&list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ)
before returning to this article.

### _Reply App Background

![](https://cdn-images-1.medium.com/max/880/1*WVxIVN4qWMnIdGy9P6hOWw.gif)

_[Reply](https://medium.com/@TJgrapes/introducing-reply-3d0b57ec6744) is a
Flutter app I built from scratch and published to the Google Play Store and
Apple App Store. This is a rebuild of [my previous native Android app
“Reply”](https://medium.com/@TJgrapes/introducing-reply-3d0b57ec6744); I rebuilt
it using Flutter to make it available on both iOS and Android.

I will use this app to provide clear, practical examples of how to implement the
core Cloud Firestore operations.

I have the [code available on
Github](https://github.com/ashtonjonesdev/reply_flutter) and you can download
the app on the [Google Play
Store](https://play.google.com/store/apps/details?id=dev.ashtonjones.reply_flutter&fbclid=IwAR337t9fh0YFMAQpwQxMu6gZTGZTx0NR8QFbvw5fSaY-RZjhPQ3OiTT9Qyw)
and the [Apple App
Store](https://apps.apple.com/us/app/reply/id1522893504?fbclid=IwAR254wWiJaeF17cSLv1N9xi85p8DSLbet5dX3jW9rlkWJnSndVF2tVlLJOo).

> **_Reply helps you easily create your own custom, pre-defined messages through
> any platform.**

With the app, you create your custom message templates which you can categorize
based on the type of message (i.e. personal, social, and business messages).
Each tab corresponds to a category.

Once you create your own messages, you can preview, send, edit, and delete them
using the main button in the bottom right corner.

![](https://cdn-images-1.medium.com/max/880/1*8YKDXaEYWWaEdaS0R_1vRw.jpeg)

### Registering and Signing in Users

You can also use Firebase Authentication to register and sign in users. If you
are implementing Firebase Authentication for the first time, I recommend reading
the [_Reply source code](https://github.com/ashtonjonesdev/reply_flutter), which
will show you how to implement the following:

* Sign in with email and password
* Sign in with Google
* Sign in with Apple
* Sign out
* Register with email and password

All of these methods can be found in the project’s [AuthService.dart
class](https://github.com/ashtonjonesdev/reply_flutter/blob/master/lib/core/services/AuthService.dart).

*❗️❗️Please note that authenticating/signing/registering a user does NOT create
a user for you in the database.*

**Signing in a user and creating a user in the database are two separate
operations you must implement.**

This can be confusing because once a user has signed in, you will see an
authenticated user with a `uid` within the Authentication tab in the Firebase
console. *These are the Firebase users. ***After a successful initial sign in or
registration, a **`FirebaseUser`** is created and signed into the app.** A
`FirebaseUser` contains data such as name (called ‘display name’), email, and an
automatically generated `uid` to identify the `FirebaseUser`.

![](https://cdn-images-1.medium.com/max/1320/1*F8VsWEptWLP5g8CQmM56MQ.png)

Before we start adding user data to the database, we *first need to create users
which we will save the user data to* in the database. This should be done after
registering/signing in to the app and *only if it is a new user*.

To create users in our actual database, *we will use the data provided by the
*`FirebaseUser`*s to create users in our database; namely, we will use the
*`uid`* and the name.*

### Checking For New Users

After a successful sign in, we should have a `FirebaseUser` signed in.

We do not need to check if it is a new user when signing in with email, because
if the account does not exist, we will show a message to first register the
account:

![](https://cdn-images-1.medium.com/max/880/1*uQx9WSV9ImlvBXr-422ZxQ.jpeg)

**To check if it is a new user when signing in, we will** **compare the creation
time stamp and last sign in time stamp of the **`FirebaseUser`. This is what we
will do for the sign in with Google and sign in with Apple options. We can do
this like so:

    (firebaseUser.metadata.creationTime
            .difference(firebaseUser
                .metadata.lastSignInTime)
            .abs() <
        Duration(seconds: 1))

Let’s take a look at how this is done when signing in with Google.

![](https://cdn-images-1.medium.com/max/880/1*Z-S8I4JRkbLDNUjAATOe0A.jpeg)

When the sign in with Google button is pressed, we will execute the following.
Notice how after the user has been authenticated (signed in) with Google, we
compare the creation time and last sign in time to check if it is a new user. If
a user is signing in for the first time, we create a new user in the database,
passing the `FirebaseUser` to the method so we can utilize the data it contains.

> **Note:*** I ran into a quirk when comparing the creation time and last sign in
> time. For some reason during the initial sign in, the time stamps were off by a
thousandth of a second, when they should have been the same. Thus, I could not
compare them using equality, because they were always off. To address this, I
compared the difference between the two, checking to see if there is at least a
one second difference.*

### Creating a New User in the Database

To create a new user in the database, we will do the following:

* Use the authenticated FirebaseUser to get the user’s name and email.
* Create a collection called “users” and add a user (a document) to it, setting
the document id as the FirebaseUser uID, the name field as the FirebaseUser’s
display name, and the email field as the FirebaseUser’s email.

If done successfully, we should see a users collection with a user document:

![](https://cdn-images-1.medium.com/max/1320/1*e86cqmK6sG9AEvJM2XQ_-A.png)

*Each document in Cloud Firestore has a document ID to identify it. We use the
*`setData`* method instead of the *`add`* method so we can explicitly set a
document ID instead of it being automatically generated. We do this to easily
retrieve the document later. However, please be aware this could cause potential
conflicts.*

When a user is signed to our app, we can call:

`FirebaseUser firebaseUser = FirebaseAuth.instance.currentUser()`

Since we set the document ID (for the document of the FirebaseUser) to be the
same as the corresponding FirebaseUser uID , we can simply retrieve the document
using the `FirebaseUser`’s `uid`:

    firestoreInstance.collection(
    ).document(firebaseUser.uid).updateData({
    // Update document
    )}

Now let’s get to the meat of this article and learn how to work with data and
the database.

### Adding Data to the Database

#### Setting Up Security Rules

Setting up Cloud Firestore Security Rules is important to keep your users’ data
secure. Furthermore, if you don’t change the initial security rules, your app‘s
Cloud Firestore database access will expire after 30 days. I recommend [reading
how to structure security
rules](https://firebase.google.com/docs/firestore/security/rules-structure) to
apply the appropriate restrictions to your app. The following set of rules
ensure that only authenticated users can read and write their own data:

#### Adding Initial Data

Before we get into the add feature of *_Reply*, let’s learn how to add data when
we first create users in the database. This is useful to have placeholder data,
such as welcome messages when the user first signs in:

![](https://cdn-images-1.medium.com/max/880/1*a49HV-RhyHUdZw2tLMEdMQ.jpeg)
<span class="figcaption_hack">Placeholder message in _Reply</span>

In this case, we want to add placeholder messages when the user is first created
so we can show welcome messages.

We are going to use the `MessageCard` model class to add messages. Notice the
`toJson()` and `fromJson()` methods. **To add custom objects to Cloud Firestore
in Flutter, we must first serialize the data. ***In this case, we are
serializing the inside the *`MessageCard`* model class, converting our objects
into a map that Cloud Firestore will accept.*

If you would like to read more about serialization in Flutter, check out [this
post](https://medium.com/codespace69/flutter-working-with-data-json-json-and-serialization-f90165b659d0).

Now we can simply add fields when we are saving the user to the database. Here
we are adding 5 new fields, one for each category of messages, and adding
MessageCards (messages) that display the user’s name, using the `toJson()`
method to serialize the data.

#### **Adding Data to an Existing Document**

![](https://cdn-images-1.medium.com/max/880/1*3bX4QXkEQrErQ7gLBq9zjA.gif)
<span class="figcaption_hack">Add Message in _Reply</span>

Adding data to Cloud Firestore can be implemented in multiple different ways.

*The *`add`*, *`setData`*, and *`updateData`*, and *`updateData`* +
*`FieldArray.union`* methods can all be used to add data.*

I recommend [reading the
documentation](https://firebase.google.com/docs/firestore/manage-data/add-data)
to fully understand when to use each add method, but here is a brief overview:

* `add()`**:** Add a document with an auto-generated id
* `setData()`**:** Add or overwrite a document with an explicit id. If the
document does not exist, it will be created. If the document does exist, its
contents will be overwritten with the newly provided data
* `updateData()`: Add fields to document without overwriting the entire document
* `updateData() + FieldArray.union():`Add an element to an array field of a
document.

#### Adding New Data to an Existing Array Field

![](https://cdn-images-1.medium.com/max/880/1*3bX4QXkEQrErQ7gLBq9zjA.gif)

Now we are going to learn how to add data. This is how data is added in *_Reply*
when a user adds a new message.

Since we created placeholder data, we already have a fields for the messages,
which are lists. In Cloud Firestore, lists are stored as arrays, so we are
actually working with an array field.

In this case, we have to update an existing array field; we must add an element
(message) to the list while retaining the existing elements (messages). To do
this, we use `updateData` + `FieldArray.arrayUnion`.

*Notice we must first serialize the data using the *`toJson()`* method to save
the data to the database. To store it as a list of items instead of a map, we
convert the map to a list.*

If executed correctly, we should see the new message added to the existing array
field. (In this case, the `personalMessages` field)

![](https://cdn-images-1.medium.com/max/1320/1*i9meNZWDHn6unv2PKoroPA.png)

### Deleting Data From the Database

![](https://cdn-images-1.medium.com/max/880/1*oorE4l3O6vn54ptlwpEdLQ.gif)

Deleting data from Cloud Firestore can be implemented in multiple different
ways.

Again, I recommend [reading the
documentation](https://firebase.google.com/docs/firestore/manage-data/delete-data)
to get a full grasp of when to use each method. Here is a brief overview:

* `delete():` Delete data from a document
* `updateData() + FieldValue.delete():` Delete a field from a document
* `updateData() + FieldArray.remove():` Remove an element from an array field
within a document

Similar to the add message case, we need to update an existing array field; we
must remove an element (message) from the list while retaining the existing
elements (messages). To do this, we use `updateData() + FieldArray.remove().`

If implemented correctly, the message should be deleted and the field,
personalMessages, should still contain the other messages. Here we removed the
the “Get Together” message, which happened to be 1st element in the array field.

![](https://cdn-images-1.medium.com/max/1320/1*vb_EiaaMSXrjjZYqq1IpJw.png)

### Editing Data in the Database

![](https://cdn-images-1.medium.com/max/880/1*SWzHUXDThYoKpQgQqNIhcQ.gif)

Continuing with our trend, editing data also has multiple implementations and
use cases. Let’s take a look:

* `updateData():` Update fields of a document without overwriting the entire
document
* `setData() with merge:true`Update fields in a document or create it if it do not
exist
* `updateData() + FieldValue.arrayRemove() + updateData() +
FieldValue.arrayUnion():`Update elements in an array field within a document
* `updateData() + FieldValue.increment():`Increment a numeric field within a
document

For the *_Reply* app, what we need to do is *replace* the old message with the
new message. To do this, we will use `updateData() + FieldValue.arrayRemove() +
updateData() + FieldValue.arrayUnion():`

*Notice what is going on here. To update the message, we are actually just
deleting the old value, then adding a new one- a clever way to edit something.*

### Getting Data From the Database

![](https://cdn-images-1.medium.com/max/880/1*Qd8kH7lBfCykh_Y5pIC-qQ.jpeg)

Now that we have data in the database, we can retrieve it. I saved getting data
for last because it is the most cumbersome and complex data operation to
implement.

Fortunately, there is really only one method we need to know when getting data:
`get()`.

Here is what we need to do to retrieve our data:

* We first create an empty `List` to hold the data we will get from our database
* We then query the database for the data we need
* Since the data is returned to us as a `Map`, we need to convert it to
`MessageCard` objects so we can easily retrieve the data; to do this, we use the
`fromJson()` method in our MessageCard class
* Once our data is converted to `MessageCard` objects, we can get the data we
need; in this case, we are retrieving the `MessageCard`s and adding them to a
list.
* Finally, we return the list of messages to show them in the UI

**Since the data returned from Cloud Firestore will be a Map, we need to convert
it to deserialize it and convert it to a custom object.** This is the opposite
of what we had to do when saving the MessageCard objects to Cloud Firestore. We
use the `fromJson()` method to do this.

### Conclusion

Let’s recap. We learned how to:

* Check for new users upon sign in
* Create new users in a Cloud Firestore Database
* Set basic security rules
* Add placeholder data for our users
* Add new data to the database
* Delete data in the database
* Edit data in the database
* Get data from the database

I know that was a lot, but you made it!

Now you can truly be a (Fire)base User. 🔥

### Resources
https://github.com/ashtonjonesdev/reply_flutter
https://codelabs.developers.google.com/codelabs/flutter-firebase/index.html?index=..%2F..index#6
https://www.youtube.com/watch?v=v_hR4K4auoQ&list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ&index=2&t=0s

<br>