Used in conjunction with FlutterFlow, PowerSync enables developers to build local-first apps that are robust in poor network conditions and that have highly responsive frontends while relying on Supabase for their backend. This guide walks you through configuring PowerSync within your FlutterFlow project that has Supabase integration enabled.

The PowerSync FlutterFlow Library is currently in the process of being published to the Marketplace. Check back here soon, or refer to our legacy guide.

New and Improved integration: Welcome to our updated FlutterFlow integration guide. This version introduces a dedicated PowerSync FlutterFlow Library, offering a simpler and more robust solution compared to the previous version which required extensive custom code.

Key improvements are:

  • Uses the new PowerSync FlutterFlow Library

  • Supports Web-based test mode

  • Streamlined Setup

  • No more dozens of custom actions

  • Working Attachments package (Coming soon)

Note that using libraries in FlutterFlow requires being on a paid plan with FlutterFlow. If this is not an option for you, you can use our legacy guide with custom code to integrate PowerSync in your FlutterFlow project.

Guide Overview

Before starting this guide, you’ll need:

This guide walks you through building a basic item management app from scratch and takes about 30-40 minutes to complete. You should then be able to use this knowledge to build and extend your own app.

  1. Configure Supabase and PowerSync Prerequisites

  2. Initialize Your FlutterFlow Project

  3. Build a Sign-in Screen

  4. Read Data

  5. Create Data

  6. Update Data (Coming soon)

  7. Delete Data

  8. Sign Out

  9. (New) Display Connectivity and Sync Status

  10. Secure Your App

    1. Enable RLS in Supabase

    2. Update Sync Rules in PowerSync

Configure Supabase

  1. Create a new project in Supabase.

  2. PowerSync uses the Postgres Write Ahead Log (WAL) to replicate data changes in order to keep PowerSync SDK clients up to date.

  3. Run the below SQL statement in your Supabase SQL Editor:

    create table
    public.lists (
       id uuid not null default gen_random_uuid (),
       created_at timestamp with time zone not null default now(),
       name text not null,
       owner_id uuid not null,
       constraint lists_pkey primary key (id),
       constraint lists_owner_id_fkey foreign key (owner_id) references auth.users (id) on delete cascade
    ) tablespace pg_default
    
  4. Create a Postgres publication using the SQL Editor. This will enable data to be replicated from Supabase so that your FlutterFlow app can download it.

    create publication powersync for table public.lists;
    

    This is a static list of tables. If you add additional tables to your schema, they must also be added to this publication.

    This guide uses the default postgres user in your Supabase account for replicating changes to PowerSync, since elevating custom roles to replication has been disabled in Supabase. If you want to use a custom role for this purpose, contact the Supabase support team.

Configure PowerSync

Create a PowerSync Cloud Instance

  1. In the Overview workspace of the PowerSync Dashboard you will be prompted to create your first instance:

If you previously created an instance, you can create a secondary instance by navigating to the Manage instances workspace and clicking on Create new instance.

  1. Give your instance a name, such as “Testing”.
  2. [Optional] You can change the default cloud region from US to EU, JP (Japan), AU (Australia) or BR (Brazil) if desired.
    • Note: Additional cloud regions will be considered on request, especially for customers on our Enterprise plan. Please contact us if you need a different region.
  3. [Optional] You can opt in to using the Beta version of the Service, which may contain early access or experimental features. Always use the Stable version in production.
  4. Click Next.

Connect PowerSync to Your Supabase

  1. From your Supabase Dashboard, select Connect in the top navigation bar:
  • In the Direct connection section, copy the complete connection string (including the [YOUR-PASSWORD] placeholder)
  • Paste the connection string into the PowerSync instance URI field. PowerSync will automatically parse this URI to populate the connection details.

  • For the Password field, enter your database’s postgres user password (this is your database/project password, not your Supabase account password). Need help? Check Supabase’s guide on managing database passwords.

  • Note: PowerSync includes Supabase’s CA certificate by default, so you can use verify-full SSL mode without additional configuration.

Your connection settings should look similar to this:

  1. Verify your setup by clicking Test Connection and resolve any errors.

  2. Click Next.

  3. PowerSync will detect the Supabase connection and prompt you to enable Supabase auth. To enable it, copy your JWT Secret from the Supabase Dashboard’s API settings and paste it into the form:

  4. Click Enable Supabase auth to finalize your connections settings.

You can update your instance settings by navigating to the Manage instances workspace, opening your instance options and selecting Edit instance.

PowerSync will now create an isolated cloud environment for your instance. This process typically takes a few minutes.

Configure Sync Rules

Sync Rules allow developers to control which data gets synced to which user devices using a SQL-like syntax in a YAML file. For the demo app, we’re going to specify that each user can only see their own lists.

  1. To update your Sync Rules, open the sync-rules.yaml file.

  2. Replace the sync-rules.yaml file’s contents with the below:

    # This will sync the entire table to all users - we will refine this later
    bucket_definitions:
       global:
          data:
                - SELECT * FROM lists
    
  3. In the top right, click “Validate sync rules” and ensure there are no errors. This validates your sync rules against your Postgres database.

  4. In the top right, click “Deploy sync rules” and select your instance.

  5. Confirm in the dialog and wait a couple of minutes for the deployment to complete.

  • For additional information on PowerSync’s Sync Rules, refer to the Sync Rules documentation.

  • If you’re wondering how Sync Rules relate to Supabase Postgres RLS, see this subsection.

Initialize Your FlutterFlow Project

  1. Create a new Blank app, give it a name, and disable Firebase.

  2. Under “App Settings” -> “Integrations”, enable “Supabase”.

    1. Enter your Supabase “API URL” and public “Anon Key”. You can find these under “Project Settings” -> “API” in your Supabase dashboard.

    2. Click “Get Schema”.

  3. Under “App Settings” -> “Project Dependencies” -> “FlutterFlow Libraries” click “Add Library”.

    1. Close the library config.

    1. Select the “PowerSync” library.

    2. Add your schema:

      1. Copy and paste the generated schema into the “PowerSyncSchema” field.

      1. On the PowerSync Dashboard, right-click on your instance and select “Generate Client-Side Schema” and select “FlutterFlow” as the language.
    3. Copy and paste your PowerSync instance URL into the “PowerSyncURL” field.

Build A Sign-In Screen

  1. Under the “Page Selector”, click “Add Page, Component, or Flow”.
  1. Select the “Auth1” template and name the page Login.

  2. Delete the Sign Up, Forgot Password and Social Login buttons — we will only be supporting Sign In for this demo app.

  1. Under “App Settings” -> “App Settings” -> “Authentication”:

    1. Enable Authentication.

    2. Set “Supabase” as the “Authentication Type”.

    3. Set the Login page you just created as the “Entry Page”.

    4. Set “HomePage” as the “Logged In Page”:

  1. In your Supabase Dashboard, under “Authentication”, click on “Add User” -> “Create new user” and create a user for yourself to test with:
  1. Test your app with test mode:

Click on 'Test'

Checkpoint: You should now be able to log into the app using the Supabase user account you just created. After logging in you should see a blank screen.

For once, a blank screen means success!

Read Data

We will now create our first UI and bind it to the data in the local SQLite database on the device.

There are three ways to read data from the SQLite database using PowerSync’s FlutterFlow library:

  1. Auto-updating queries for Layout Elements with Dynamic Children e.g. the ListView Element

    • This uses the library’s PowerSyncQuery component
  2. Auto-updating queries for basic Layout Elements e.g. Text Elements

    • This uses the library’s PowerSyncStateUpdater component
  3. Once-off reads for static data

    • This uses the library’s PowerSyncQueryOnce custom action

Prepare Supabase Tables for Reads

For reading data in FlutterFlow, you need a Custom Function per Supabase table to map Supabase rows to data that can be used by the library. This is because FlutterFlow Libraries do not support Supabase classes.

  1. Navigate to “Custom Code” and add a Custom Function.

  2. Name the function supabaseRowsToList (if your Supabase table name is “Customers”, you would name this supabaseRowsToCustomers).

  3. Under Function Settings, set the “Return Value” to Supabase Row

    1. Check “Is List”.

    2. Uncheck “Nullable”.

    3. Specify lists as the “Table Name”.

  4. Also under Function Settings, click “Add Arguments”.

    1. Set its “Name” to supabaseRows

    2. Set its “Type” to “JSON”.

    3. Check “Is List”.

    4. Uncheck “Nullable”.

  5. In the Function Code, paste the following code:

    /// MODIFY CODE ONLY BELOW THIS LINE
    return supabaseRows.map((r) => ListsRow(r)).toList();
    
  6. Click “Save Function”.

Custom function to map the lists table from Supabase

1. Auto-updating queries for Layout Elements with Dynamic Children

Create a Component to display List Items

  1. Under the “Page Selector”, click “Add Page, Component, or Flow”.

  2. Select the “New Component” tab.

  3. Select “Create Blank” and call the component ListItems.

  4. Under the “Widget Palette”, drag a “ListView” widget into the ListItems component.

  5. Still under the “Widget Palette”, drag a “ListTile” into the ListView widget.

    The ListItems component should now look like this

  6. Under the “Widget Tree”, select the ListItems component.

    1. At the top right under “Component Parameters” click “Add Parameters”.

    2. Click “Add Parameter”.

    3. Set its “Name” to lists.

    4. Set its “Type” to Supabase Row.

    5. Check “Is List”.

    6. Specify lists as the “Table Name”.

    7. Click “Confirm”.

  7. Still under the “Widget Tree”, select the “ListView” widget.

    The ListItems component should now look like this

    1. Select the “Generate Dynamic Children” panel on the right.

    2. Set the “Variable Name” to listItem.

    3. Set the “Value” to the component parameter created in the previous step (lists).

    4. Click “Confirm”.

    5. Click “Save”.

    6. Click “Ok” when being prompted about the widget generating its children dynamically.

  8. Still under the “Widget Tree”, select the ListTile widget.

    Set the list item title text

    1. In the “Properties” panel on the right, under “Title”, click on the settings icon next to “Text”.

    2. Set as “listItem Item”.

    3. Under “Available Options”, select “Get Row Field”.

    4. Under “Supabase Row Fields”, select “name”.

    5. Click “Confirm”.

  9. Repeat Step 8 above for the “Subtitle”, setting it to “created_at”.

The ListItems component should now look like this

Display the List Component and populate it with Data

  1. Under the “Page Selector”, select your HomePage.

  2. Under the “Widget Palette”, select the “Components and custom widgets imported from library projects” panel.

  3. Drag the PowerSyncQuery library component into your page.

  4. In the Properties panel on the right, under “Component Parameters” -> “child”:

    1. Click “Confirm”.

    2. Click “Confirm”.

    1. Click on “Unknown”.

    2. Select ListItems we previously created.

    3. Click on lists.

    4. Set the “Value” to “Custom Functions” > supabaseRowsToList we created previously.

    5. Under the supabaseRows argument, set the “Value” to “Widget Builder Parameters” > rows.

  5. Still under “Component Parameters” add the SQL query to fetch all list items from the SQLite database:

    1. Paste the following into the “sql [String]” field: select * from lists order by created_at;

    2. For this query there are no parameters - this will be covered further down in the guide.

  6. Still under “Component Parameters”, check “watch [Boolean]”. This ensures that the query auto-updates.

Test your App

  1. Check that there are no project issues or errors.

  2. Reload your app or start another test session.

  3. Notice that your homepage is still blank. This is because the lists table is empty in Supabase. Create a test row in the table by clicking on “Insert” -> “Insert Row” in your Supabase Table Editor.

    1. Leave id and created_at blank.

    2. Enter a name such as “Test from Supabase”.

    3. Click “Select Record” for owner_id and select your test user.

Checkpoint: You should now see your single test row magically appear in your app:

2. Auto-updating queries for basic Layout Elements

In this section, we will be making the ListView component clickable and navigate the user to a page which will eventually display the list’s To-Do items. This page will show the selected list’s name in the title bar (“AppBar”). This uses Page State and the PowerSyncStateUpdater library component.

Create a Page Parameter

This parameter will store the selected list’s ID.

  1. Under the “Page Selector”, click “Add Page, Component, or Flow”.
  1. Create a blank page and name it Todos.

  2. Under the “Widget Tree”, select your Todos page.

  3. At the top right of the “Properties” panel on the right, click on the plus icon for Page Parameters.

  4. Click “Add Parameter”.

  5. Set the “Parameter Name” to id.

  6. Set the “Type” to “String”.

  7. Click “Confirm”.

Create a Local Page State Variable

This variable will store the selected list row.

  1. Still in the “Widget Tree” with the Todos page selected:

  2. Select the “State Management Panel” on the right.

  1. Click on “Add Field”.

  2. Set “Field Name” to list.

  3. Set the “Type” to “Supabase Row”.

  4. Select lists as the “Table Name”.

  5. Click “Confirm”.

Bind the Page Title to the Page State

  1. Under the “Widget Palette”, select the “Components and custom widgets imported from library projects” panel.

  2. Drag the PowerSyncStateUpdater library component into your page.

  3. Under the “Widget Tree”, select the PowerSyncStateUpdater component.

  4. In the “Properties” panel on the right, under “Component Parameters”:

    1. Add the SQL query to fetch the selected list from the SQLite database. Paste the following into the “sql [String]” field: select * from lists where id = :id;

    2. Click on “parameters [Json]” select “Create Map (JSON)” as the variable.

      1. Click “Confirm”.

      1. Under “Add Map Entires”, click “Add Key Value Pair”.

      2. Set the “Key” to id.

      3. Set the “Value” to the page parameter created previously called id.

  5. Still under “Component Parameters”, configure the “onData” action:

    1. Click “Confirm”.

    2. Close the Action Flow Editor.

    1. Open the “Action Flow Editor”.

    2. Select the “Callback” trigger type.

    3. Click “Add Action”.

    4. search for “update page” and select “Update Page State”.

    5. Click “Add Field”.

    6. Select your list page state variable.

    7. Set “Select Update Type” to “Set Value”.

    8. Set “Value to set” to “Custom Functions” > supabaseRowsToList.

    9. Set the “Value” to “Callback Parameters” > rows

    10. Click “Confirm”.

    11. Under “Available Options”, select “Item at Index”.

    12. Set “List Index Options” to “First”

  6. Still under the “Widget Tree”, select the “AppBar” -> “Text” widget.

    1. In the “Properties” panel on the right, click on settings icon next to “Text”.

    2. Click on “Page State” > “List”.

    3. Set “Supabase Row Fields” to “name”.

    4. (Optional) Set the “Default Variable Value” to List Name.

    5. Click “Confirm”.

Make the ListView Component Clickable

  1. Under the “Page Selector”, select your ListItems component.

  2. Under the “Widget Tree”, select the ListTile widget.

  3. In the “Actions” panel on the right, click “Add Action”. “On Tap” should be selected by default.

  4. In the “Navigate” subsection, select “Navigate To”.

  5. Select the “Todos” page.

  6. Under “Parameters” click “Pass”.

  7. “id” should be auto-selected, click on it.

  8. Click on the settings icon next to “Value”

  9. Set it to “listItem Item”.

  10. Under “Available Options” select “Get Row Field”

  11. Under “Supabase Row Fields” select “id”.

  12. Click “Confirm”.

  13. (Optional) Enable the back button to navigate back:

    1. Under the “Page Selector”, select your Todos page.

    2. Under the “Widget Tree”, select the “AppBar” component.

    3. In the “Properties” panel on the right, enable “Show Default Button”.

Test your App

Instant Reload your app or start another test session.

Checkpoint: You should now be able to click on a list item and it should navigate you to a new page showing the name of the list in the title bar:

3. Once off reads for static data

This section is a work in progress. Please reach out on our Discord if you have any questions.

Create Data

You will now update the app so that we can capture new list entries.

  1. Under the “Page Selector”, select your HomePage page.

  2. Under the “Widget Palette”, search for “float” and drag the “FAB” widget onto your page.

  3. In the “Actions” panel on the right, click “Add Action”.

    1. Under “Custom Action” > “PowerSync”, select “powersyncWrite”.

    2. Under the “Set Action Arguments” > “sql” section, add the SQL query to create a new list item. For the purpose of this guide we are hardcoding the list’s name, normally you would build UI for this.

      1. Paste the following into the “Value” field: INSERT INTO lists(id, created_at, name, owner_id) VALUES(uuid(), datetime(), 'new item', :userId);
    3. Under the “parameters” section, set the userId parameter we’re using the above query:

      1. Click on “UNSET”.

      2. Select “Create Map (JSON)” as the variable.

      3. Under “Add Map Entires”, click “Add Key Value Pair”.

      4. Set the “Key” to userId.

      5. Set the “Value” to “Authenticated User” > “User ID”.

      6. Click “Confirm”.

Checkpoint: Reload your app and click on the + floating action button. A new list item should appear, which also automatically syncs to Supabase:

Update Data

This section is a work in progress. Please reach out on our Discord if you have any questions.

Delete Data

In this section we will add the ability to swipe on a ListTile to delete it.

  1. Under the “Page Selector”, select your ListItems component.

  2. Under the “Widget Tree”, select the ListTile widget.

  3. In the “Properties” panel on the right, enable “Slidable”.

  4. Click “Open Slidable”.

  5. Select the “SlidableActionWidget”.

  6. In the “Actions” panel on the right, click “Add Action”.

    1. Under “Custom Action” > “PowerSync”, select “powersyncWrite”.

    2. Under the “Set Action Arguments” > “sql” section, add the SQL query to delete the list item.

      1. Paste the following into the “Value” field: delete from lists where id = :id;
    3. Under the “parameters” section, set the id parameter we’re using the above query:

      1. Click on “UNSET”.

      2. Select “Create Map (JSON)” as the variable.

      3. Under “Add Map Entires”, click “Add Key Value Pair”.

      4. Set the “Key” to id.

      5. Set the “Value” to “listItem Item”.

      6. Under “Available Options” select “Get Row Field”.

      7. Under “Supabase Row Fields” select “id”.

      8. Click “Confirm”.

      9. Click “Confirm”.

Checkpoint: Reload your app and swipe on a list item. Delete it, and note how it is deleted from the list as well as from Supabase.

Sign Out

  1. Navigate to “Custom Code” and create a new Custom Action called signOut without Arguments or Return Values and paste the below code:
// Automatic FlutterFlow imports
import '/backend/supabase/supabase.dart';
import "package:power_sync_y2dtqn/backend/schema/structs/index.dart"
    as power_sync_y2dtqn_data_schema;
import 'package:ff_theme/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'package:power_sync_y2dtqn/custom_code/actions/initialize_power_sync.dart'
    as ps;

Future signOut() async {
  final database = await ps.getOrInitializeDatabase();
  //await database.disconnectAndClear(); // this will completely delete all the local data, use with caution as there may be items still in the upload queue
  await database
      .disconnect(); //this will simply disconnect from the PowerSync Service and preserve all local data
}

// Set your action name, define your arguments and return parameter,
// and then add the boilerplate code using the green button on the right!
  1. Click “Save Action”.

  2. Under the “Page Selector”, select your HomePage page.

  3. Under the “Widget Palette”, drag a “Button” onto the right of your “AppBar”.

  4. In the “Properties” panel on the right, rename the “Button Text” to Sign Out.

  5. Switch to the “Actions” panel and open the “Action Flow Editor”.

  6. Select “On Tap” as the action trigger.

  7. Click “Add Action” and add a call to the signOut Custom Action.

  8. Chain another Action and call to “Supabase Authentication” > “Log Out”:

  1. Click “Close”.

Checkpoint: You should now be able to reload your app and sign out and in again.

(Optional) Display Connectivity and Sync Status

The PowerSync library provides a built-in component that displays real-time connectivity and synchronization status. Since the sync state is available globally as part of your app state, you can easily monitor the database status throughout your application. To add this status indicator:

  1. Under the Widget Palette, select the “Components and custom widgets imported from library projects” panel.

  2. Drag the PowerSyncConnectivity component into your home page’s “AppBar”.

Secure Your App

PowerSync’s Sync Rules and Supabase’s support for Row Level Security (RLS) can be used in conjunction. Here are some high level similarities and differences:

  • RLS should be used as the authoritative set of security rules applied to your users’ CRUD operations that reach Postgres.

  • Sync Rules are only applied for data that is to be downloaded to clients — they do not apply to uploaded data.

    • Sync Rules can typically be considered to be complementary to RLS, and will generally mirror your RLS setup.

Enable RLS in Supabase

Run the below in your Supabase console to ensure that only list owners can perform actions on the lists table where owner_id matches their user id:

alter table public.lists
  enable row level security;

create policy "owned lists" on public.lists for ALL using (
  auth.uid() = owner_id
)

Update Sync Rules

Currently all lists are synced to all users, regardless of who the owner of the list is. You will now update this so that only a user’s lists are synced to their device:

  1. Navigate to the PowerSync Dashboard and open your sync-rules.yaml file.

  2. Delete the existing content and paste the below contents:

bucket_definitions:
  user_lists:
    parameters: select request.user_id() as user_id
    data:
      - select * from lists where owner_id = bucket.user_id
  1. Click on “Validate”.

  2. Click on “Deploy sync rules”.

  3. Wait for the deploy to complete.

Checkpoint: Your app should continue running seamlessly as before.

Known Issues, Limitations and Gotchas

Below is a list of known issues and limitations.

  1. Deploying to the Apple App Store currently requires some workarounds due to limitations in FlutterFlow:

    1. Download the code from FlutterFlow.

    2. Open the Podfile located in the ios/ directory.

    3. The following option in the Podfile needs to be updated from use_frameworks! :linkage => :static to use_frameworks! (remove everything after the exclamation sign).

    4. After removing that option, clean the build folder and build the project again.

    5. You should now be able to submit to the App Store.

  2. Exporting the code from FlutterFlow using the “Download Code” action in FlutterFlow requires the same workaround listed above.

  3. Other common issues and troubleshooting techniques are documented here: Troubleshooting.