Skip to content
FlutterFlow+Supabase
Stack Integration

FlutterFlow + Supabase Integration Guide

FlutterFlow's Supabase integration covers auth and CRUD out of the box. Custom RLS policies, Edge Functions, and real-time listeners require going beyond the visual builder.

Use Cases
  1. Supabase Auth (email/password, OAuth) wired in FlutterFlow
  2. Fetching and displaying Supabase Postgres rows in list views
  3. Inserting and updating records from FlutterFlow actions
  4. Triggering Supabase Edge Functions via FlutterFlow custom actions
Implementation

Connect Supabase in FlutterFlow Settings → Supabase. Add your Project URL and anon key. For Row-Level Security: RLS policies apply to all FlutterFlow queries automatically — configure them in the Supabase dashboard before building. For complex queries (joins, RPCs), use Supabase Edge Functions and call them via FlutterFlow's API call action. Custom real-time listeners require a custom Dart action using the Supabase Flutter SDK directly.

In detail

How the pieces connect

FlutterFlow generates a Flutter app that talks to Supabase over the supabase_flutter package. The visual builder wires your Project URL and anon key into a Supabase.initialize() call at startup, so every query and auth action runs client-side from the device. There is no FlutterFlow backend in the middle — the app hits Supabase's PostgREST API (auto-generated from your Postgres schema), GoTrue for auth, and Realtime over WebSockets directly.

That means Postgres is your single source of truth. FlutterFlow's query collections, list views, and form actions map onto tables and columns you define in the Supabase dashboard. Auth state lives in the supabase_flutter session, which the SDK persists locally and refreshes automatically. When you outgrow the visual query builder — joins, aggregates, RPC functions — you call a Postgres function or an Edge Function rather than restructuring the app.

Production gotchas

RLS is the one that bites first. Because the anon key ships inside the app, anyone can read it. Row-Level Security is the only thing standing between a user and every row in a table. If you build screens in FlutterFlow before writing policies, queries silently return empty (or, worse, with RLS off, return everything). Write select/insert/update/delete policies keyed on auth.uid() before wiring the UI.

Second: session refresh on mobile. supabase_flutter refreshes JWTs in the background, but a token can expire while the app is backgrounded for long periods — handle the onAuthStateChange stream so signed-out states route correctly instead of throwing on the next query.

Third: Edge Function secrets. Service-role keys and third-party API keys belong in Function secrets, never in a FlutterFlow custom action — the client bundle is readable.

Realtime and complex queries beyond the builder

FlutterFlow's visual queries handle straightforward filtering and ordering, but they don't expose Supabase Realtime. To push live updates into a screen, add a custom Dart action that calls Supabase.instance.client.channel(...) (or .from(table).stream(primaryKey: [...])) and subscribes to Postgres changes. You'll need Realtime enabled on the table and an RLS policy that allows the change events through — Realtime respects RLS the same way reads do.

For anything relational, push the logic into Postgres. A create function ... returns table RPC called through FlutterFlow's API call (or the SDK's .rpc()) keeps joins and filtering on the server, where the database does the work and the client just renders the result. Use Edge Functions when you need to run TypeScript — calling external APIs, verifying webhooks, or doing work that shouldn't trust the client.

When this combo fits — and when it doesn't

FlutterFlow plus Supabase fits cross-platform apps that are mostly CRUD over a relational schema: directories, marketplaces, internal tools, MVPs that need real auth and a real database fast. You get a Postgres backend, row-level access control, and a native iOS/Android/web build from one project, without the overhead of assembling the same stack at an agency.

It fits less well when most of your value is custom backend logic or heavy offline-first sync — at that point you're writing so many custom Dart actions and Edge Functions that the visual layer adds friction instead of speed. It's also a poor match if you need a backend that isn't Postgres-shaped. For relational data with clear ownership rules, though, the pairing is hard to beat on time-to-shipped.

Other integration guidesView all →
Related

Need this built?