Links

Build a Notes App with FlutterFlow and Supabase

In this hands-on guide, we'll walk you through the process of building a notes app using FlutterFlow, with Supabase powering its backend. In this app, we will work with authentication with Supabase, create and update data to our Supabase tables and also integrate with the storage bucket in Supabase to store images that we add in our notes. The app will have these four key screens:

LoginPage

This is our gateway, facilitating user login or sign-up. Every user account created here would get registered in your Supabase database's Users table.

HomeListPage

After successful authentication, users will be brought to this screen. Here, they'll find all their notes, pulled directly from our database's Notes table.

NoteDisplayPage

A simple click on any note takes the user to this screen, where they can see the content of their selected note and view associated details.

CreateNotePage

This is where users create a note, which eventually gets added to the notes table in our database.
We already have a starter project ready for you to begin. Clone this project and follow the steps below.

Setting up the Supabase Database

Before diving into the screens and UI, we need to set up the database tables that will store our users and notes data. Let's do this in Supabase.

1. Creating a new project in the Supabase dashboard

Start by going to the Supabase homepage. Click on "Start a project" to open the Supabase dashboard, or just log in here.
Next, create a new project in your organization. Give your project a name and set a database password (make sure it's a strong one!). Then, choose a region for your database server and click on "Create new project".
Creating a new project
Once your project has been created successfully, you'll be taken to your project settings page. This page displays your Project API keys.

2. Creating the users table

Next, head over to your Table Editor
and create a new table named users. If you need guidance, you can refer to the Supabase Setup section in our documentation, or consult the Supabase docs directly to learn how to create a table in Supabase. The structure of your table should resemble the screenshot provided below.
The 'id' field requires a foreign key relation. You can establish this by clicking on
next to id. This will open a new window, allowing you to edit the foreign key relation. The options should appear as follows:

3. Creating the notes table

Next, we create another new table and name it notes. The schema of the notes table will be as follows:
Schema of the notes table

4. Creating a storage bucket in Supabase for storing images

To store images related to our notes, we'll need to set up a storage bucket in Supabase. Create a public bucket and name it 'notes'.
Your storage bucket will need a policy to manage access to your files. To create a new policy, go to Configuration > Policies > Your Bucket > Create a new policy for Insert. Name your policy, then add the following to the 'WITH CHECK' expression field:
((bucket_id = 'notes'::text) AND (lower((storage.foldername(name))[1]) = (auth.uid())::text))
This ensures that the policy is applied to the 'notes' bucket and the first part of the file path (the folder name) in lower case is the same as the authenticated user's ID.
For more information on Supabase's storage buckets, go to their docs.​

Connect your FlutterFlow app with Supabase

Now that our Supabase database is setup, we can start adding our front-end features of the app. Clone the starter project and let's begin!

Authenticating with Supabase

Step 1

In the project you've cloned, take a look at the left panel. You'll see all the starter screens: LoginPage, HomeListPage, NoteDisplayPage, and CreateNotePage. When you launch the app for the first time and there's no authenticated user, the app will take the user to the LoginPage to authenticate. Once the authentication process is successful, the user will be redirected to the HomeListPage.

Step 2

Before we dive into the app's logic, let's ensure all the settings are configured correctly. Navigate to Settings > App Settings > Authentication section and turn on Authentication. This will reveal additional authentication properties, such as the Authentication Type - in our case, it should be Supabase. Also, update the 'Initial Page' section with the following pages:
In this case, Entry Page is the authentication page with sign up and sign in forms and Logged In Page is the landing screen after successful login.
You also have a warning that Supabase has not been set up yet. So let's fix that in the next step.

Step 3

On the same Settings page, go to the 'Integrations' section, click on 'Supabase' and turn on 'Enable Supabase'. You'll need to add the API URL and Anon Key, which can be found on your Supabase Project's settings page. You can locate these keys on your home landing page or under 'API' on the Settings page.
After that, click 'Get Schema' to retrieve all the schema details from the tables you created in your Supabase database.
And just like that, your FlutterFlow project is now linked with your Supabase database! Great job!

Step 4

Now, let's return to our UI Builder and open the LoginPage. This page was created using our pre-made templates, which offer a quick and efficient way to build screens for popular use cases, like login. Using templates can significantly speed up your development process, so you can focus on customizing and perfecting your app.
If you want to learn more about templates, check out the documentation for detailed information.
Now, let's add our sign-up logic so that users can create an account in our app. If you look at the sign-up form, there are four fields: name, email address, password, and confirm password. Lastly, we have the 'Create Account' button, which will trigger the authentication action and send this data to our users table.
To set this up, click on the 'Create Account' button, create a new action 'On Tap', and enter the following properties:
If this is the first time you are adding an Action, please read more about it here​
​
Step 5
You also notice that there is a message right below this that warns you that defining this action will not automatically create a user row in a Supabase table so you will have to do that action separately. So let's create a new action following the first one. The type is Backend/Database > Supabase > Create Row where the Action Type is Insert Row and the table is users.
This will prompt you to set all the fields related to your user which is id, email, name, created_at. The name, email is set from the Widget State > relevant text field variables. The created_at is set from Global Property > Current Time. And the id is set from Authenticated User object created automatically on implementing the Supabase authentication step in the last action.
Check the following process on how to set the id field from the Authenticated User object.
Setting the id field
Step 6
Now let’s create the action for the Sign In button. The action is the same, but the authentication type would be Login this time. Set the Auth Provider and provide values to the email and password fields from the Widget State.
Step 7
To complete the authentication flow, we must implement a logout functionality and the steps are very similar to the previous steps. To perform logout, go to the HomeListPage and click on the logout icon button on the AppBar. Create an Auth action and select the type as Logout and there is nothing else you need to do after this. FlutterFlow will handle the logout functionality internally like other logic blocks. You can run the app at this stage and check if the authentication flow works as expected.
​

Connecting with the data: Retrieving & displaying data in the UI

Step 8

Great, we've sorted out the authentication part. Now let's turn our attention to displaying the notes in our app. We've already got our first note list item - we've named it NoteItem. But we need to fill our listview with data from our notes table. This means creating a NoteItem for each note in the table.
To do this, go to the Backend Query tab on the right. Select 'Supabase Query' as the Query Type. Then update the table name to notes and set the query type to 'List of Rows'. Everything else can stay the same. You should see a warning popup at this stage. It's just to let you know that FlutterFlow will generate the listview's children based on the data from the tables.

Step 9

The final step is fetching the results. However, the list view item is not connected to these results yet. To link them, click on the first Text widget with the value 'Note Title' and set the value 'from variable'.
You'll see that
has been added to the list of sources. You can now adjust the Text widget to pull its value from the 'Available Options' under the 'notes Row'. Here, we can select 'title'.
For the 'Date' Text widget, choose the created_at option in the same way. But there's an extra feature here - FlutterFlow lets you easily change the format of the date to suit your needs.

Step 10

By now, if there's any data in the notes table, it should be shown as a list on this screen. But we want more than that. We want to be able to tap on these items to open the NoteDisplayPage, where we can see the full note description, title, and any related images. So, let's add that navigation functionality.
This is simple to do. Choose the first list item from your widget tree and add a new Action to it. The action type is navigation and the page you're navigating to is the NoteDisplayPage. This page will need one parameter, which is the current note object, or the 'notes Row'. When you go to define the parameter, FlutterFlow will swiftly take you to the UI Builder, right to the NoteDisplayPage. It will prompt you to add the parameter you want to send from the previous page. This is a really handy feature of FlutterFlow - it reminds you when you're missing a step needed to successfully pass objects between pages.
The properties for the receiving parameter should be added like this:
Now, passing the Supabase Row from the HomeListPage to the NoteDisplayPage is a breeze. Your Action properties should appear like this:

Step 11

Now, we need to ensure that the NoteDisplayPage displays the UI using the data it receives. All you need to do is connect it with the notes data sent from the previous page.
Let's see if you can manage this on your own. Give it a try!
Hint: In this page, the source of data will be under the Page Parameters.
​

Connecting with the data: Adding data to the tables

Step 12

Our app is now set up to display data from the notes table. But currently, we can't create a note, so the table remains empty. On our HomeListPage, we have a FloatingActionButton in the bottom right corner with the label Create. When users tap this button, it will take them to the CreateNotePage.
Setting this up is similar to the previous navigation action we made. The only difference is that we don't need to send a parameter this time.
Step 13
Our CreateNotePage is already equipped with two text fields for gathering the title and description from users, an icon button at the bottom, and an image widget. Now, let's add an action for when the icon button is tapped.
The action type is Utilities > Upload/Save Media. Make sure to update the Upload Type to Supabase. You'll need to provide the bucket name, which in this case is 'notes', as created in the Supabase dashboard. The other properties are as follows:
Step 14
After you've completed the previous step, a new Widget State will be added to this screen. If you navigate to your image widget and replace the dummy path URL with 'Set from Variable', you'll see a new option in the Widget State source called 'Uploaded File URL'. Now, the image path is set based on the URL of the uploaded file.

Step 15

Users have the option to attach an image to their note. If an image is uploaded, it should be displayed on this screen so users can review it before they create the note and send the data to the database. But right now, if you were to run the app at this stage, it would throw an error. An exception would be triggered because the URL is null when no image has been uploaded.
To fix this, we'll use a feature called Conditional Visibility. This will ensure that the image is only shown if there's an uploaded file. You'll find the Conditional Visibility option in the top right corner under the Properties Tab. Enable a Single Condition that checks if the uploaded file URL from the Widget state is set and not empty. When you run the app now, the exception should no longer appear.

Step 16

We now have all the data we need to create our first note and send it to the database. It's time to assign the relevant actions to the 'Create' button. Open the Action Flow Editor - we'll be implementing two actions here.
The first action is Backend/Database > Supabase > Insert Row. The values of the various variables will be pulled from the Widget state, except for the 'created_at' property, which will come from the Global Properties. This action will create a new row in our notes table using the provided user details.
Once the row is successfully created, we should return to our homepage to see the updated list. So, we'll add another action following the last one: Navigation > Navigate Back.
​
Congratulations! You've successfully made your way through this tutorial and set up your own note-taking app, powered by Supabase. You now have the skills to add and display data, handle various actions like seamless navigation, and upload media files to Supabase storage buckets. We also did a livestream with Supabase on FlutterFlow's channel and built the same Notes app, so check it out if you're interested
We appreciate you taking the time to follow this guide. Your success and feedback are important to us. If you have any comments or suggestions about this tutorial, please let us know through the feedback form at the top of this page. Your input will help us create better and more useful guides in the future.
Thank you, and happy app building!