Production-grade Flutter Web rewrite of the SplitLLM React frontend. Features Supabase Authentication, Riverpod state management, and Dio-based API integration with the existing Flask backend.
| Layer | Technology |
|---|---|
| Framework | Flutter 3.x (Web + Android + iOS) |
| State Management | Riverpod |
| Routing | go_router |
| HTTP Client | Dio |
| Auth | Supabase Authentication |
| Config | flutter_dotenv (.env) |
| Charts | fl_chart |
| Hosting / CI | Vercel |
- Flutter SDK ≥ 3.2.0
cd flutter-app
flutter pub getRuntime configuration lives in a .env file (loaded at startup via flutter_dotenv). Copy the template and fill in your values:
cp .env.example .env# .env
SUPABASE_URL=https://YOUR_PROJECT.supabase.co
SUPABASE_ANON_KEY=sb_publishable_xxx # publishable/anon key — safe in client
API_BASE_URL=http://localhost:8081.env is git-ignored. To change Supabase or the backend URL later, just edit .env
(local) or the Vercel Environment Variables (production) — no Dart code changes needed.
Resolution order for each value:
.env→--dart-define=KEY=value→ built-in default.
flutter run -d chrome.env is picked up automatically. You can still override per-run with
--dart-define=API_BASE_URL=https://your-api.domain.com if you prefer.
flutter build web --release --tree-shake-iconsOutput is written to build/web.
The app deploys to Vercel via vercel.json + vercel_build.sh.
Because Vercel's build image has no Flutter SDK, the build script installs Flutter,
writes a .env from the project's Environment Variables, then runs flutter build web.
Setup:
-
Import the repository into Vercel (Root Directory =
flutter-app). -
Vercel reads
vercel.jsonautomatically:- Build command:
bash vercel_build.sh - Output directory:
build/web
- Build command:
-
In Project Settings → Environment Variables, add:
Variable Description SUPABASE_URLSupabase project URL SUPABASE_ANON_KEYSupabase publishable / anon key API_BASE_URLProduction backend API base URL -
Push to your default branch — Vercel builds and deploys automatically.
vercel.json also configures SPA rewrites (all routes → index.html) and security
headers (X-Frame-Options, Strict-Transport-Security, etc.).
lib/
├── main.dart # Entry point (loads .env, inits Supabase)
├── app.dart # MaterialApp.router
├── core/
│ ├── constants/constants.dart # Config resolved from .env / dart-define
│ ├── models/ # Data models
│ │ ├── user_model.dart
│ │ ├── event_model.dart
│ │ ├── expense_model.dart
│ │ ├── share_model.dart
│ │ └── filter_input_model.dart
│ ├── services/
│ │ ├── api_client.dart # Dio + Supabase token interceptor
│ │ └── auth_service.dart # Supabase Auth abstraction
│ ├── router/router.dart # go_router with auth guard
│ ├── providers.dart # Top-level Riverpod providers
│ ├── utils/
│ │ ├── app_theme.dart # Material 3 dark theme
│ │ ├── date_utils.dart # Date formatting
│ │ └── helpers.dart # Utilities
│ └── widgets/shell_screen.dart # Responsive nav shell
└── features/
├── auth/presentation/login_screen.dart
├── dashboard/presentation/dashboard_screen.dart
├── events/presentation/
│ ├── events_screen.dart
│ ├── event_detail_screen.dart
│ └── create_event_screen.dart
├── expenses/presentation/
│ ├── create_expense_screen.dart
│ ├── expense_detail_screen.dart
│ └── share_bill_screen.dart
├── friends/presentation/
│ ├── friends_screen.dart
│ ├── friend_detail_screen.dart
│ └── add_friend_screen.dart
├── personal_expenses/presentation/
│ └── personal_expenses_screen.dart
├── account/presentation/account_screen.dart
└── settlements/presentation/payment_screen.dart
All runtime config (SUPABASE_URL, SUPABASE_ANON_KEY, API_BASE_URL) is resolved
at startup, in order of precedence:
- the bundled
.envfile (flutter_dotenv), - a compile-time
--dart-define=KEY=value, - a built-in default.
# Local dev: values come from .env
flutter run -d chrome
# Override a single value at build time
flutter build web --release --dart-define=API_BASE_URL=https://api.domain.com
# Production: Vercel injects env vars → vercel_build.sh writes them into .envThe Flutter app sends Authorization: Bearer <supabase_access_token> in all API
requests. The Flask backend validates the Supabase JWT (e.g. against the project's
JWKS / JWT secret) and reads the user id from the sub claim.
- Analyze React repository
- Create Flutter project structure
- Core infrastructure (Dio, Supabase Auth, Riverpod, go_router)
- Data models (User, Event, Expense, Share, FilterInput)
- Auth feature (Login, Sign-up, Google Sign-In, Forgot Password)
- Dashboard (Summary cards, Pie chart, Quick actions)
- Events (List, Detail, Create/Edit, Delete)
- Expenses (Create/Edit, Detail, Share Bill)
- Friends (List, Detail, Add, Delete, Settle)
- Personal Expenses (List, LLM Chatbot)
- Account (Profile, QR Code, UPI, Change Password, Sign Out)
- Payment page (Public payment with QR)
- Vercel config (vercel.json, vercel_build.sh)
- Environment config via
.env(flutter_dotenv) - Update Flask backend to validate Supabase JWTs
- Set Vercel Environment Variables for deployment