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-KEYWithout 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:
{
"SUPABASE_URL": "https://YOUR-PROJECT.supabase.co",
"SUPABASE_ANON_KEY": "YOUR-ANON-KEY"
}flutter run -d chrome --dart-define-from-file=env.jsonenv.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/previewMounts 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| Suite | Covers |
|---|---|
test/shell_layout_test.dart | The three layouts at real device sizes, the pane-width arithmetic, menu highlighting, and that no two features claim the same route. |
test/routing_test.dart | Where 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
| Target | Command | Notes |
|---|---|---|
| Web | flutter build web --release | What ships to store.mblrc.me. |
| Android | flutter build apk | Debug APK is large (~150 MB); release is a fraction of that. |
| iOS | flutter build ios --no-codesign | Signing 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.
- 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| Secret | What it is |
|---|---|
SUPABASE_URL | The project URL. |
SUPABASE_ANON_KEY | The publishable client key. |
CLOUDFLARE_API_TOKEN | Needs "Cloudflare Pages: Edit". |
CLOUDFLARE_ACCOUNT_ID | The 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.