In this tutorial we're going to take the Flutter example that Supabase provides and rewrite it using VGV tooling, such as Very Good CLI, Flutter Bloc, layered architecture, and the most important thing, 100% test coverage 🧪.
This tutorial is based on the Quickstart: Flutter tutorial that Supabase has on their website. It will target Android, iOS, and the web.
Let's get started! 🙌
But first, what is Supabase?
Supabase is an open source Firebase alternative. As a quick overview, Supabase offers you a bunch of services, like authentication with different providers (email, Apple, Azure, Discord, GitHub, GitLab, Bitbucket, Slack and much more), database (PostgreSQL), storage, functions, etc.
If you're looking to have more auth providers than the typical ones (email/password, Twitter, Facebook, Google, and Apple) or a SQL database instead of a NoSQL database, you could consider Supabase for your project. To learn more about Supabase, visit their official website here.
These are the things that we are going to cover in this tutorial:
- Login with email using deep linking.
- Handle user status (authenticated or unauthenticated) to know where we need to navigate using Flow Builder.
- Create a simple database with a script on the Supabase SQL Editor console.
- Add rules to the database.
- Update user information in the database.
Supabase initial configuration
Create a new project
The first step is to visit the Supabase website and create a login. When we have access to the dashboard, we can create a new organization and project.
- Click on New Project and select your organization (if you don't have one, you should create a new one by clicking on New organization). The next step is to fill out the form to create a new project.
- We can now see the new project on our dashboard.
Create a database with SQL Editor
To create a new database in our project, we should navigate to SQL Editor where we can create a script to create the table and the rules to apply in this database.
In this case, we are going to create a table called account. We need to modify the User Management Started script and add the following code:
Then, we need to click on RUN in the right bottom corner. Finally we have our new database created.
Supabase table editor
Once the database is created, we can navigate throughout the dashboard to view the table and rules.
In the left menu, we can navigate to table editor to see the account table. It is currently empty, but you can see that the table has three columns id, username and companyname.
If you want to check the rules that we created with the script, you can click on RLS enabled to see them.
As I mentioned at the beginning, the authentication method that we are going to use in this tutorial is email deep linking.
We need to add a redirect URL in our authentication dashboard (this is only if your application is on the web).
In this case, we are going to use the redirect URL that Supabase provides in the Flutter example. You should add the provider redirect URL if you are building a real project.
Now, we have everything set up and the next step is to create our Flutter project! 🙌
Flutter project and packages
In this section we are going to create the Flutter project and different packages using Very Good CLI.
The first step is to activate Very Good CLI.
Once we have Very Good CLI activated, we can create the Flutter project. We just have to run the following line in the terminal.
Which packages do we need for this project? 🤔
I've created a few of them under the package folder (you should create them in your application root).
- Supabase auth client: this package is in charge of the sign-in and sign-out methods on Supabase.
- Supabase database client: this package is in charge of get user information from the database and updating user information.
- User repository: this repository is going to have the ability to use the SupabaseAuthClient to call the sign-in and sign-out methods, use the SupabaseDatabaseClient to get user profile information, and update the user on Supabase.
- Form inputs: this package hosts the different form inputs to use on the view.
- Email launcher: opens a default email app on Android and iOS.
You can visit the GitHub repository to see the code of the Form inputs and Email launcher packages. Here I'm only going to cover the packages related to Supabase.
Let's create them! 😀
Supabase auth client
First we need to run the following command:
Then we should modify the pubspec.yaml file to add the necessary dependencies.
Now we can add the logic for this package. In this part we are going to be focused on the sign-in and sign-out methods.
Supabase database client
First you need to create the package.
Then modify the pubspec.yaml file.
The next step is to add the logic to this package. In this case, this package is going to be in charge of retrieving user information from the account table and also to updating the data on the account table.
First, we need to create a SupabaseUser model. To do that, we can create a new folder inside src called models. Then add the new supabase_user.dart file inside it. For this model I used the Json Serializable package.
The next step is to add the logic in the supabase_database_client.dart file.
The first step is to create the package.
The second step is to modify the pubspec.yaml file to add the dependencies.
Now we can add the logic for this package. In this repository we can use the SupabaseAuthClient and SupabaseDatabaseClient to be able to use the methods inside of each class. With this repository we can do the following:
- Fetch User information
- Update user
We're finished configuring the packages. Now we can jump into the business logic and views.
Note: These packages need different workflows to work with GitHub workflows. We can find these files in the .github folder in the example repository.
Supabase in Flutter
The first step is to configure Supabase in our project. First we need to add a few native configurations on Android and iOS.
- Android: add these lines to the AndroidManifest.xml file.
- iOS: add these lines to the Info.plist file.
Prepare the Flutter project to use Supabase
Here we need to do a few things. First, we need to update the pubspec.yaml file in our project to be able to use all the packages that we just created, as well as the Supabase dependency.
The second step is to prepare the main_development.dart file to use the necessary packages and initialize Supabase.
As you might have noticed, to initialize Supabase, we need to provide the supabase url and the anon key, which can be found in the project settings.
Create the features
In this project, we are going to have four features:
- Auth States Supabase: here we are going to add two classes that will tell us if a user is authenticated or not and if the authentication is required. These classes are also going to be used in our widgets.
- App: this is going to be in charge of listening to the user status (authenticated or unauthenticated) to determine whether to navigate to the login page or account page.
- Login: this feature is going to host all of the sign-in logic for this project.
- Account: this feature is going to get the user information and display them into a text field. It will also update the information in the Supabase database and handle the sign-out.
This feature is in charge of recovering the Supabase session to know if a user was authenticated in the previous session or not. With this information, this feature is going to determine where the application should navigate.
We are going to use Flow Builder to handle this navigation. Here we have the following structure:
- AppView: here we are extending the class with an AuthStateSupabase and not a StatefulWidget. This is important to know if the user is authenticated.
- Routes: this method onGenerateAppViewPages is used in our AppView.
This feature is in charge of sign-in and checking the email text field using the form inputs package that we created.
The structure is:
- LoginView: here we are going to have all the widgets needed to log in to our app. Also, we are going to use a AuthStateSupabase class to know if the user is currently logged in or not. In addition, we are going to use the input form package to check if the email is correct.
- LoginBloc: the main aim of this class is communicating with the UserRepository to do login. Also, here we are going to handle all of the possible states of the email text field when the user changes the value.
This feature is in charge of multiple things:
- Retrieving information from the database.
- Updating user information in the database.
- Signing out.
- Checking the inputs of the userName and companyName text fields. The structure is:
- AccountView: this class is going to extend to AuthRequiredState, which determines if the user is logged in. This is necessary because we are going to show the user account information.
- AccountBloc: this class is in charge of multiple events. Here we need to use UserRepository to retrieve the user information, update the database, and sign out. Also, we have other events to handle user interaction with the userName and companyName text fields.
It's time to see everything working together! Fingers crossed 🤞
It's working! 🎉
Extra: Personalize your email body content
You can personalize the body of the email that you send after sign-in. It is quite easy to do with Supabase. Go to the project settings in your Supabase console and follow the steps.
Supabase is a great tool if you are looking for login options beyond the typical ones like email/password, Twitter, Facebook, Google, and Apple. You can see here all the auth providers that Supabase can provide.
Also, if you prefer to manage your data in a SQL database, it is a great choice because Supabase uses a PostgreSQL database, in contrast with Firebase. You can find more info in the official documentation.
I highly recommend you check all the options that Supabase provides because there are tons of them with extensive documentation. Here are a few reference links:
Hope you enjoyed it! If you are interested in testing this application, you can find all the tests working in the GitHub repository.
Thanks for your time! 😃 Happy coding!
Felix Angelov and Óscar Martín contributed to this article.