Simple Recipes App made in Flutter — Firestore

Michael Krol
Dec 8, 2018 · 8 min read

How to store your favorite recipes in a Firestore database by using Dart and Flutter.

In the previous article we have created a Firebase project for our recipes app and implemented the authentication with Google Sign-In. Firebase offers us many other tools that help us to develop apps and improve their quality. Today we are going to see how to include Firestore into our project. Firestore is a cloud NoSQL database.

Photo by Maarten van den Heuvel on Unsplash

This series of articles is a step-by-step guide of the creation of a simple recipes app in Flutter and Dart. This guide does not require any previous experience in Dart and Flutter. If you are familiar with principles of object-oriented programming, you are ready to follow this series of articles. Previous articles of this series describe how to develop the user interface of the login page and the list view of our recipes app. Furthermore, we have learned how to include Firebase into our project and authenticate users with Google by using Firebase Authentication.

You can find final results of particular articles in branches of the recipes_app repository on github. If you have already read previous articles, we are ready to continue the implementation.


Firestore — Setup

First of all, we need to setup our Firebase project for the usage of Firestore. It’s very simple. All you have to do is to follow a couple of steps described below. Before executing these steps login to Firebase and choose your project. As a first step, we are going to create a new database.

Database

After you have chosen your Firestore project click on “Database”.

After that click on “Create database”.

It’s sufficient to create the database in test mode first. We would not do it if we run the app in production, but it is fine for development. You can find more about Cloud Firestore security rules here.

Cool. We have successfully created a Firestore database for our app. Congratulations. Now, let’s add data of our recipes to our brand new database.

Documents and Collections

A Firestore database is based on collections and documents. Each collection contains several documents. Each document contains multiple key-value pairs. Since Firestore is a NoSQL database we are not going to work with columns and rows.

Click on “Add collection” to create a new collection. We are going to call it “recipes”.

Let’s move to the next step and add data to the created collection. So far we have been getting data by using the method getRecipes. The method getRecipes returns a list of Recipe objects based on hardcoded data in lib/utils/store.dart. Now, we are going to add recipes to our brand new Firestore collection “recipes” as documents. Each document is going to represent a recipe.

Here is an overview of the fields that our recipe documents are going to contain:

On the screenshots below you can see an example of creating a new document for a recipe.

Click on “Save” to finish the creation of the document.

Now repeat these steps for other recipes. Feel free to add as many recipes as you wish.

Nice. Now, after we have added data of all recipes we are ready to start with the implementation.


Implementation

We are going to use the cloud_firestore package as Dart library for the implementation today.

Dependencies

Let’s add the required package to our project by including it into the dependencies section in pubspec.yaml first.

Now, run flutter packages get to get the new package and to be able to use it as a Dart library.

Recipe class

In order to be able to work with data from Firestore and Recipe objects we are going to implement a constructor fromMap in the class Recipe. The method fromMap is going to deserialize data that we are going to receive from Firestore and initialize a new Recipe object.

Let’s add the constructor fromMap to the class Recipe in recipe.dart.

lib/model/recipe.dart

In the previous article we have learned how to store app’s state and pass the state down the widget tree using InheritedWidget. So far we have stored data of the state in a State object. By now, the State class contains properties isLoading and user.

State

In order to store users’ favorites add a new property favorites of data type List<String> to the State class.

lib/model/state.dart

Since data of our recipes is stored in Firestore we do not need the getRecipes method in lib/utils/store.dart anymore.

Update Firestore Database

Delete the code in store.dart and put the implementation of the method updateFavorites that follows into the file store.dart.

Why do we need the method updateFavorites?

Here is what the new method is going to do:

The method updateFavorites is going to contain the entire logic of adding or deleting entries of users’ favorites.

Here is the implementation:

lib/utils/store.dart

As next, we are going to add a new method called getFavorites to the class _StateWidgetState.

Initialize State

We are going to use getFavorites to get user’s favorites on the initialization of the app’s state. As soon as the current user is signed-in with Google we are going to load his or her favorites.

In the code that follows you can see the implementation of the new method getFavorites and how we use it within the method signInWithGoogle.

lib/state_widget.dart

Let’s move to the implementation of the list view in the widget of the home screen. There are a couple of changes we need to implement.

Home Screen

Since the method _handleFavoritesListChanged in _HomeScreenState does not update data in Firestore yet, we need to change it, too. We are going to implement the following scenario in _handleFavoritesListChanged:

Take a look at the implementation.

lib/ui/screens/home.dart

In the last step we are going to change the method _buildTabsContent in home.dart so we are able to get recipes’ data from our cloud database.

We are going to change parameters of the private method _buildRecipes first. Change parameters of this method to optional parameters RecipeType recipeType and List<String> ids. Notice the syntax that we are using for optional named parameter in the code that follows. An optional named parameters are wrapped by curly brackets.

At the beginning of the method _buildRecipes we define a reference to the source of recipes’ data. In our case it is an CollectionReference to the previously created collection “recipes”. After that we declare a new Stream<QuerySnapshot> object called stream. Depending on whether an argument for the parameter recipeType has been passed we define stream so we get recipes of passed type or we get all recipes from the stream later on.

If we take a look at the implementation below we can see that the ListView.builder that we have been using before has been replaced by StreamBuilder. The StreamBuilder listens to the stream and returns a ListView containing RecipeCard widgets.

What about the parameter ids? We are going to use ids to get RecipeCard widgets only for recipes contained in the list of favorites which are being stored in the favorites array in the State class.

Before a RecipeCard is being returned by the StreamBuilder we check if ids is set. If so, we check if the recipe’s ID is contained in the list passed to parameter ids.

Here is the code:

lib/ui/screens/home.dart

Now, delete unused variables in _HomeScreenState.

Well done! We have finished the implementation.

Let me summarize what we have learned today.


Conclusion

We have learned how to:

In the next article we are going to implement a settings widget that we are going to use in the settings view and finish the development for this series of articles.

Thank you for reading. I wish you happy fluttering!

Flutter Community

Articles and Stories from the Flutter Community

Michael Krol

Written by

Trying to get a better software developer everyday.

Flutter Community

Articles and Stories from the Flutter Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade