DEV Community

Ashton Jones
Ashton Jones

Posted on

Cloud Firestore Basics in Flutter: How to Get, Add, Edit, and Delete Data in Cloud Firestore, Demonstrated in a Real Flutter App

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
and check out this
code lab to setup Firebase with
Flutter
,
check out the _Reply project as a
guide
, and watch this video
to learn about documents and collections in Cloud
Firestore

before returning to this article.

_Reply App Background

_Reply 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”
; 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
and you can download
the app on the Google Play
Store

and the Apple App
Store
.

_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.

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, 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
.

❗️❗️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.

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:

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.

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:

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
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:


Placeholder message in _Reply

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
.

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


Add Message in _Reply

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

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

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)

Deleting Data From the Database

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

Again, I recommend reading the
documentation

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.

Editing Data in the Database

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:trueUpdate 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

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 MessageCards 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

Top comments (1)

Collapse
 
mk320 profile image
Mukund Kukreja

Thank You :))