Exploring data operations using cloud_firestore in Flutter

Darshan Kawar
Flutter Community
Published in
9 min readSep 12, 2022

--

In this article, we will take a look at some basic concepts used in FlutterFire’s cloud_firestore plugin and how to make use of various methods offered by it for data operations such as adding and retrieving, so let’s get on with it straight-away.

Agenda

Below are the areas of the plugin I will cover today:

  • What is a Collection, how to create one and refer it while querying the data?
  • What is a Document, how to create one and refer it while querying the data?
  • What is a QuerySnapshot, DocumentSnapshot and AsyncSnapshot ?
  • How to retrieve data from a collection based on different conditions?

Note: This article will not cover how to create a new Flutter app and a Firebase project and how to integrate these two. I will assume that there’s already a Flutter app integrated with Firebase with all required settings and configuration done. For more details and reference, please follow this documentation to create a new Flutter app, integrate Firebase project with firestore database created, add required plugins in project such as firebase_core and cloud_firestore so that the app is in running state.

Starting point

Once we have the app running, first step is to initialise the FlutterFire using it’s CLI tool to integrate your Firebase project with your Flutter app using flutterfire configure command, which in turn, writes the native configuration files to the root of your Flutter project.

flutterfire configure generates firebase_options.dart file and puts it inside lib folder of your project and then we simply need to refer it by calling initializeApp method on Firebase class as below in our main method:

Initialize Firebase using Dart.

You can read more about this CLI tool here.

Data to work with

We will create data in the form of documentsprogrammatically using the methods offered by the plugin. So, we will work on to create following data:

Set of data to add

In order to add above data in firestore db, we will first need to know some basic terms that we are going to use / see today. They are:

  • Collection: A collection is a top-most level node in hierarchy of a firestore database. Think of it as a starting point for a data stored and has a name, in our case, we will name it city_data . In other words, consider collection to be a name of a table from a traditional database point of view.

Since firestore is a NoSql database, it stores the data in the form of key-value pair. The actual data, is stored in the form of a document which forms the next level of node in hierarchy, below collection . Each document will have an entry of holding data of a different city.

With above details, we will start to create a collection now. In order to do that, we will use CollectionReference class and create an object of it to refer throughout our data creation and other operations.

collection creation

After creating a collection object, we will now see what are the different methods offered by it and we will print them to see what they represent.

There are several other methods offered by it, but it will make sense to go over them after we add some data to it.

List of methods for collection

Let’s now create first document with city data by declaring a var and then adding required key and value pair like below:

creating first document

We will then pass cityOne to add() method like below to add our first document in the db:

add first document to firestore db

After running our Flutter app, in the firestore database created in Firebase console, it creates the collection city_data and adds the first document with data as above. Just refresh the browser to see the newly created collection and respective document inside it as below:

Likewise, we will create 4 more documents, so that we will have a handful of data to work with:

Now, in order to read all above documents, we will make use of get() method as below and we will print all documents with respective document fields:

Likewise, we can also reference a particular document field by using DocumentReference class, create an object of it and see what methods are offered for it which we can use.

Let’s use some of the methods listed above, print them and see what they return:

We will cover some of the methods below to make more sense of their usage.

Filtering data based on conditions

In production applications, users sometimes want data specific to some conditions, so in this case, we will use filtering on the data created and print that data. Some of the filtering methods provided are orderBy() , where() , limit()and equality operators that we will see below.

orderBy()

This method is used to display the data in a specified order, either descending or ascending . The method orderBy takes required argument of field followed by optional descending taking a bool . Accordingly, we will query the database to print the result in descending order of city , as below:

Observe that city is sorted descending.

Note that we used QuerySnapshot class which is a snapshot returned as a query result. The snapshot contains DocumentSnapshot objects. It exposes docs method which returns all documents from the snapshot that are present in DocumentSnapshot .

A DocumentSnapshot is a snapshot that returns data of a single document . It exposes data property through which the data is extracted.

We can combine limit() method with orderBy() to restrict the number of documents to retrieve / display, like below:

where()

This method is used to query and retrieve data based on specific condition. From our data, we will retrieve a document whose capital:true , as below:

The Equality operators are an optional field we can provide in where() method which helps to further narrow down our query. Below are the equality operators offered:

We will see another example with isGreaterThan and will retrieve the data based on population , as below:

Similarly, we can make use of isLessThan , isEqualTo and other equality operators listed.

We can also combine orderBy with where clause to retrieve data based on certain condition. For example, show data whose capital is not true , as below:

Important thing to note in above query is that, whenever we are using orderBy and where together, we need to query it on same field, ie, capital in above example, otherwise, if we pass different fields in both clauses, we will get below error:

Reading the error message carefully, it states, The initial orderBy() field ‘[[FieldPath([city]), false]][0][0]’ has to be the same as the where() field parameter ‘FieldPath([capital])’ when an inequality operator is invoked. Indicating that we can make use of orderBy() field again after initial query and pass different field to it, as below:

In order for above query to work properly, make sure you have already created a composite index in Firebase console as below, else, it will throw an error indicating to create one. The error message has a link to create the required index so just follow along the link.

Let’s see another example by using whereIn method along with where clause, which returns all documents that matches the fields we provide inside whereIn . Consider it as a OR logical operation.

The query using whereIn returns data which matches either condition we pass in whereIn . For example, if there’s no data matching either of condition, it will return the data that matches the condition and will not throw an error, as below:

In above example, there’s no data matching country as Canada , but the query returned the one matching with Aus . Similarly we can try a query with whereNotIn .

Similarly, there are arrayContains and arrayContainsAny conditions inside where clause which essentially works on array data type, ie, these conditions will work only on array datatype. An example using arrayContainsAny is below:

While arrayContainsAny expects List<Object> and works as above, the same query will not work for arrayContains which expects Object directly, so, in order to use arrayContains , we will modify above query that will properly return results, as below:

Displaying data in UI

In above examples, we printed the query results in console and now we will see how to make use of Flutter’s widgets to display retrieving data in UI. Let’s do that and display using ListTile as below:

Wherein, _cityStream is declared as a QuerySnapshot Stream as below:

final Stream<QuerySnapshot> _cityStream = FirebaseFirestore.instance.collection('city_data').snapshots();

In above code snippet, we used AsyncSnapshot which is used for all async operations. It exposes ConnectionState like none , waiting and done to properly handle the conditions at various stages while retrieving the data. The class also helps us to handle conditions when there is an error or when the data source is empty and based on such conditions, we can decide what to do.

Running the code, the UI will show data as below:

With this, we covered what are the various methods offered by cloud_firestore plugin and how we can make use of them to retrieve data stored as part of collection and document .

That’s all I have for today. Thanks for reading and feel free to comment below your thoughts / suggestions / feedback on this topic.

I can be reached via Twitter and LinkedIn.

You may check out my profile below for other articles I wrote on Flutter.

https://medium.com/@darshankawar

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm

--

--

Darshan Kawar
Flutter Community

Open Source Support Engineer For Flutter @nevercodeHQ. Android Nanodegree Certified. Previously, Android Automation Test Engineer.