Useful Commands
The commands you will actually type while working on a Floot project, and the directory each one belongs in.
Almost every mistake on this page is a directory mistake: the Supabase CLI wants
to be run from supabase/, Deno wants to be run from supabase/functions/, and
Flutter wants the project root. So the page is grouped by where you stand rather
than by tool.
From the project root
Regenerate the translations
flutter gen-l10nReads l10n.yaml, so the .arb catalogs come from
lib/core/presentation/l10n and the Dart lands in
lib/core/presentation/l10n/generated. Those generated files are committed:
edit an .arb, run this, and commit both halves together, or the app builds
against the old strings.
Run the app
flutter runOn the web, pick port 3000 explicitly:
flutter run -d chrome --web-hostname 127.0.0.1 --web-port 3000That port is not arbitrary. site_url in supabase/config.toml is
http://127.0.0.1:3000, and it is where every confirmation and password-reset
email points. On any other port those links land nowhere.
Run the app tests
flutter testCheck your toolchain
floot requirements check
floot requirements installcheck reports what is missing or too old, scoped to the layers you
generated; install fixes it. See the CLI reference
for the full flag list.
From supabase/
Everything in this section is run from the supabase/ directory. The Supabase
CLI resolves config.toml, .env.local and every relative path against it.
Start and stop the local stack
supabase start
supabase stopsupabase start auto-loads supabase/.env.local; there is no --env-file flag
on it.
There is one restart you have to know about:
supabase stop --no-backup && supabase startThe [db.vault] block in config.toml seeds five secrets into the Vault from
.env.local, but only when the database volume is created. A plain
supabase stop && supabase start restores the saved volume and skips seeding
entirely, so an edited secret is silently ignored and local database webhooks
keep failing signature checks. --no-backup destroys the volume, which is what
makes the next start reseed. See
Troubleshooting
for the full symptom list.
--no-backup throws your local database away. Migrations and seeds rebuild
the schema, but any row you typed in by hand is gone.
Rebuild the database
supabase db resetReplays every migration, then the seeds: [db.seed] points at
db_seeds/*.sql, and storage_seeds/ is loaded into the assets bucket. Use
it after writing a migration, and before the database tests. Migration
filenames are stamped at floot create time, so your project's history starts
at its own creation moment.
Serve the Edge Functions
supabase functions serve --env-file .env.local --import-map ./functions/deno.json --no-verify-jwtLeave it running while you develop: transactional email, Stripe checkout and account deletion all go through it.
Regenerate the database types
supabase gen types --local > functions/_shared/database.types.tsRun it after any migration or db_seeds change, and after a supabase db reset;
_shared/database.types.ts is generated output, never hand-edited.
Do not pass -s / --schema
Omitted, the CLI follows api.schemas in config.toml, which is app,
graphql_public, pgmq_public and public. A hand-picked list silently
drops schemas the Edge Functions type against, and there is no stripe
schema to add: a pgTAP test asserts that no foreign table, foreign server or
wrappers extension exists, so naming one only produces an error.
Run the database tests
supabase db reset && supabase test dbsupabase test db runs every .sql file under supabase/tests/database/
through pgTAP, installing the extension for the run and dropping it afterwards.
Paths are sorted flat and lexicographically, which is the whole reason for the
naming.
The db reset in front is not optional in practice: the suite asserts against
the seeded plans and prices, and each file rolls its own work back but a
half-migrated database fails everything.
From supabase/functions/
deno.json lives here, so Deno picks up the import map and the tasks
automatically. Run these from supabase/functions/, not from supabase/.
Type-check every function
deno task checkShorthand for deno check .. This is also what proves the email templates are
translated: every Record<Language, ...> block that is missing a language is
reported with a file and a line.
Run the Edge Function tests
deno task testShorthand for deno test -A --env-file=.env.test. .env.test ships with your
project and holds deterministic, well-formed dummy credentials, so the suite
passes offline and never touches a real Stripe or SMTP account. It is not
.env.local, and the flag is --env-file=, not --env=.
Stripe
Forward webhook events to the local stack
stripe listen --forward-to localhost:54321/functions/v1/stripe-webhookForwards live test-mode events to your local stack. Each run mints a new
signing secret; copy it into STRIPE_WEBHOOK_SECRET in supabase/.env.local
and restart supabase functions serve, or every event fails its signature
check.
Provision the billing portal configurations
Two re-runnable scripts create the Billing Portal Configurations the app needs.
Both are run from the project root, and read supabase/.env.local for your
secret key.
deno run --allow-net --allow-env --env-file=supabase/.env.local \
supabase/scripts/create_restricted_portal_config.tsIt clones your account's default configuration with quantity removed from
default_allowed_updates, so a customer cannot edit their seat count from
Stripe's hosted UI. Paste the printed id back:
STRIPE_PORTAL_CONFIGURATION_ID="bpc_..."Seat-based billing only
This section applies only to projects generated with `--seat-based-billing`.
Requires ENABLE_SEAT_BASED_BILLING
Seat-based billing is off in a new project. Set `ENABLE_SEAT_BASED_BILLING=true` in both `.env.local` and `supabase/.env.local` to use this section.
deno run --allow-net --allow-env --env-file=supabase/.env.local \
supabase/scripts/create_confirm_portal_config.tsThe mirror image: it adds quantity, for the app's own plan-switch flow,
which Stripe rejects with a 400 under the restricted configuration. See
Seat-Based Billing for why the app needs
its own configuration.
STRIPE_PORTAL_CONFIRM_CONFIGURATION_ID="bpc_..."Each runs once per Stripe account and per mode: test and live keys have separate configurations, so a project that goes live needs the pair again. Stripe explains why one configuration cannot serve both flows.
Android
Keep using localhost for the local Supabase API
The Android emulator treats localhost as itself, not your dev machine, so
SUPABASE_URL=http://127.0.0.1:54321 in the app's .env.local fails from
inside the emulator. Reverse port forwarding lets you keep that URL instead of
switching to 10.0.2.2.
adb devicesLists the emulators and physical devices ADB can see, each with a device id.
adb -s <device-id> reverse tcp:54321 tcp:54321Any connection from inside the emulator to 127.0.0.1:54321 or
localhost:54321 is now intercepted by ADB and forwarded to port 54321 on
your machine, where the local Supabase API is listening. See
Troubleshooting for the 10.0.2.2 alternative,
which needs no forwarding but requires a separate .env.local value for
Android.
Note
This forwarding rule is temporary. If the emulator or ADB restarts, run the
adb reverse command again.
Simulators & desktop
All three of these open a deep link straight in a running simulator or
emulator, or launch the app fresh on macOS if it is not already running, which
beats retyping the link in a browser. The scheme is the one floot create
derived from your --org: com.acme.myapp for --org com.acme on a project
named my_app.
/usr/bin/xcrun simctl openurl booted "com.acme.myapp://oauth-callback"adb shell am start -a android.intent.action.VIEW -d "com.acme.myapp://oauth-callback"open "com.acme.myapp://oauth-callback"Requires the scheme to be registered in Info.plist; see
Deep Links if it does not respond.