StoreManagement

Build & deploy

One codebase, three targets, one workflow.

Running it locally

Supabase credentials are compile-time constants, not a .env read at runtime — a web build has no server to keep a secret on, and the anon key is a public client key by design.

cd app

flutter run -d chrome \
  --dart-define=SUPABASE_URL=https://YOUR-PROJECT.supabase.co \
  --dart-define=SUPABASE_ANON_KEY=YOUR-ANON-KEY

Without them main() trips an assert that says exactly what’s missing, rather than failing later with a confusing network error.

Typing those every time invites a typo that reads as a Flutter bug — one missing hyphen and you get Could not find an option named "dart-define", which sounds like the flag doesn’t exist. Keep them in a file instead:

app/env.json
{
  "SUPABASE_URL": "https://YOUR-PROJECT.supabase.co",
  "SUPABASE_ANON_KEY": "YOUR-ANON-KEY"
}
flutter run -d chrome --dart-define-from-file=env.json

env.json is in .gitignore, so it stays on your machine.

Looking at the layout without credentials

flutter build web -t tool/shell_preview.dart -o build/preview

Mounts the shell with stub pages and no Supabase at all. Useful for checking the three layouts at tablet and phone widths.

Tests

cd app
flutter analyze
flutter test        # 137 tests
SuiteCovers
test/shell_layout_test.dartThe three layouts at real device sizes, the pane-width arithmetic, menu highlighting, and that no two features claim the same route.
test/routing_test.dartWhere signing in lands, that the root is not a screen, and that every redirect from the old three-app paths points at a route that exists.
test/cash/The daily-close arithmetic as pure functions, the commission report, the live panel, phone layout.
test/stock/Receive rows, stock-out menu order, editing a day that already has sales, day report, phone layout.
test/books/Posting credits, journal export, phone layout.

Overflow is a test failure

A RenderFlex overflow throws in a widget test. The mobile_layout suites pump real pages at phone size, so a layout that doesn’t fit fails in CI rather than on the tablet at the counter.

Targets

TargetCommandNotes
Webflutter build web --releaseWhat ships to store.mblrc.me.
Androidflutter build apkDebug APK is large (~150 MB); release is a fraction of that.
iOSflutter build ios --no-codesignSigning needs an Apple Developer account. file_saver doesn't yet support Swift Package Manager, which Flutter warns about but still builds.

Deploy

Pushing to main runs the workflow: analyze, test, build web, publish to Cloudflare Pages. The tests gate the deploy, so a build that compiles but fails its tests doesn’t reach the shop.

.github/workflows/deploy.yml
- name: Test
  working-directory: app
  run: |
    flutter pub get
    flutter analyze
    flutter test

- name: Build web
  working-directory: app
  run: |
    flutter build web --release --base-href / \
      --dart-define=SUPABASE_URL=${{ secrets.SUPABASE_URL }} \
      --dart-define=SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }}

- name: Deploy to Cloudflare Pages
  uses: cloudflare/wrangler-action@v3
  with:
    command: pages deploy app/build/web --project-name=mblrc-store --branch=main
SecretWhat it is
SUPABASE_URLThe project URL.
SUPABASE_ANON_KEYThe publishable client key.
CLOUDFLARE_API_TOKENNeeds "Cloudflare Pages: Edit".
CLOUDFLARE_ACCOUNT_IDThe account id.

If a change doesn't appear in production

Check the Actions run before suspecting the code — a red deploy step leaves the previous build serving happily, and an expired CLOUDFLARE_API_TOKEN is the usual cause. Note also that the app registers a service worker, so an already-open tab can keep serving the old bundle after the CDN has updated; a hard reload clears it.

Database changes

Add a numbered file to supabase/migrations/ and apply it. Never edit one that’s already been applied — a correction is a new migration. The app assumes the current head; a page whose migration hasn’t been applied will fail to load rather than silently show nothing.