Firestore Todos with “flutter_bloc”
⚠️ This article may be out of date. Please view the updated tutorial at bloclibrary.dev.
Hey everyone! In this tutorial, we’re going to build a reactive Todos App which hooks up to Firestore.
We’re going to be building on top of the flutter todos example so we won’t go into the UI since it will all be the same. Instead, we’re going to focus on the repository layer as well as some of the blocs.
Our finished product will look very similar to the previous todos example, however, our todos will be synced across all devices and will update in real-time.
Before we get started, I want to give a special thanks to warriorCoder for helping make this tutorial possible!
Repositories
We’ll start off in the repository layer with the TodosRepository
.
Todos Repository
Create a new package at the root level of our app called todos_repository
.
Note: The reason for making the repository a standalone package is to illustrate that the repository should be decoupled from the application and can be reused across multiple apps.
Inside our todos_repository
we need to create the following folder/file structure.
├── lib
│ ├── src
│ │ ├── entities
│ │ │ ├── entities.dart
│ │ │ └── todo_entity.dart
│ │ ├── models
│ │ │ ├── models.dart
│ │ │ └── todo.dart
│ │ ├── todo.dart
│ │ ├── todo_entity.dart
│ │ ├── todos_repository.dart
│ │ └── firebase_todos_repository.dart
│ └── todos_repository.dart
└── pubspec.yaml
Dependencies
The pubspec.yaml
should look like:
Note: We can immediately see our todos_repository
has a dependency on firebase_core and cloud_firestore.
Package Root
The todos_repository.dart
directly inside lib
is responsible for the public exports in our package and should look like:
Entities
Entities represent the data provided by our data provider.
The entities.dart
file is a barrel file that exports the single todo_entity.dart
file.
Our TodoEntity
is the representation of our Todo
inside Firestore.
Let’s create todo_entity.dart
and implement it.
The toJson
and fromJson
are standard methods for converting to/from json. The fromSnapshot
and toDocument
are specific to Firestore.
Note: Firestore will automatically create the id for the document when we insert it. As such we don’t want to duplicate data by storing the id in an id field.
Models
Models will contain plain dart classes which we will work with in our Flutter Application. Having the separation between models and entities allows us to switch our data provider at any time and only have to change the the
toEntity
andfromEntity
conversion in our model layer.
Our models.dart
is another barrel file which exports our single todo
model.
Next, we need to create our todo.dart
model.
It’s important to note that having a model gives us the flexibility to change providers. Our application will always interact with our model (not our entity) so if we change data providers all we need to do is update our toEntity
and fromEntity
methods without needing to refactor our entire application.
Todos Repository
TodosRepository
is our abstract base class which we can extend whenever we want to integrate with a differentTodosProvider
.
Let’s create todos_repository.dart
Note: Because we have this interface it is easy to add another type of datastore. If, for example, we wanted to use something like sembast all we would need to do is create a separate repository for handling the sembast specific code.
Firebase Todos Repository
FirebaseTodosRepository
manages the integration with Firestore and implements ourTodosRepository
interface.
Let’s create firebase_todos_repository.dart
and implement it!
That’s it for our TodosRepository
, next we need to create a simple UserRepository
to manage authenticating our users.
User Repository
Create a new package at the root level of our app called useer_repository
.
Inside our user_repository
create the following folder/file structure.
├── lib
│ ├── src
│ │ └── user_repository.dart
│ └── user_repository.dart
└── pubspec.yaml
Dependencies
The pubspec.yaml
should look like:
Note: We can immediately see our user_repository
has a dependency on firebase_auth
.
Package Root
The user_repository.dart
directly inside lib
should look like:
User Repository
UserRepository
is our abstract base class which we can extend whenever we want to integrate with a different provider`.
Let’s create user_repository.dart
and implement it!
Firebase User Repository
FirebaseUserRepository
manages the integration with Firebase and implements ourUserRepository
interface.
Let’s open firebase_user_repository.dart
and implement it!
That’s it for our UserRepository
, next we need to setup our Flutter app to use our new repositories.
Flutter App
Setup
Let’s create a new Flutter app called flutter_firestore_todos
. We can replace the contents of the pubspec.yaml
with the following:
Note: We’re adding our todos_repository
and user_repository
as external dependencies.
Authentication Bloc
Since we want to be able to sign in our users, we’ll need to create an AuthenticationBloc
.
Note: If you haven’t already checked out the flutter firebase login tutorial, I highly recommend checking it out now because we’re simply going to reuse the same AuthenticationBloc
.
Authentication Events
Authentication States
Authentication Bloc
Now that our AuthenticationBloc
is finished, we need to modify the TodosBloc
from the original todos tutorial to consume the new TodosRepository
.
Todos Bloc
The main difference between our new TodosBloc
and the original one is in the new one, everything is Stream
based rather than Future
based.
When we load our todos, we are subscribing to the TodosRepository
and every time a new todo comes in, we dispatch a TodosUpdated
event. We then handle all TodosUpdates
via:
Putting it all together
The last thing we need to modify is our main.dart
.
The main difference is we’ve wrapped our entire application in a MultiBlocProvider
which initializes and provides the AuthenticationBloc
and TodosBloc
. We then, only render the todos app if the AuthenticationState
is Authenticated
using BlocBuilder
. Everything else remains the same as in the previous todos tutorial
.
That’s all there is to it! We’ve now successfully implemented a firestore todos app in flutter using the bloc and flutter_bloc packages and we’ve successfully separated our presentation layer from our business logic while also building an app that updates in real-time.
The full source for this example can be found here.
If you enjoyed this exercise as much as I did you can support me by ⭐️the repository, or 👏 for this story.