A Flutter web app that photographs a receipt, extracts items / prices / GST / service charges with Gemini Vision, and splits the bill across any number of people with a simplified debt-transfer summary.
Live app: https://bill-splitt.web.app
- Take or upload a photo of a receipt.
- Gemini AI parses it into items, quantities, and charges.
- Tap each person's avatar next to the items they ordered (long-press for a shared quantity).
- The app calculates each person's share — charges are compounded in receipt order (service charge first, then GST on the new total).
- The Summary screen shows a per-person breakdown and the minimum transfers needed to settle the bill.
| Tool | Version |
|---|---|
| Flutter SDK | ≥ 3.11 |
| Dart SDK | ≥ 3.11.5 |
| Node.js | ≥ 18 |
| Firebase CLI | latest (npm i -g firebase-tools) |
flutter pub get
flutter run -d chromeClick Use sample receipt to open the review flow immediately with fixed hawker-stall data. You can also pick any image and click Analyze; mock mode ignores the image contents and returns the same fixed receipt. No Firebase project, API key, or backend is required.
flutter pub getcd functions
npm install
cd ..firebase login
firebase use bill-splitt # or: firebase use --add to pick/create a projectThis repo does not track lib/firebase_options.dart. If you need Firebase
client initialization for local work, generate it on your machine:
dart pub global activate flutterfire_cli
flutterfire configureThe generated file stays local and is gitignored. See
lib/firebase_options.example.dart for the expected shape.
- Go to https://aistudio.google.com/app/apikey and sign in.
- Click Create API key → choose an existing Cloud project or create a new one.
- Copy the
AIza…key. - Restrict the key to Generative Language API under API restrictions — Google requires this from 2026-06-19.
The key lives only on the server (Cloud Function). Never put it in the Flutter bundle or commit it to git.
Full walkthrough: docs/gemini-setup.md
firebase functions:secrets:set GEMINI_API_KEY
# Paste the AIza… value at the promptDocument AI extracts raw receipt text before the first Gemini call, so Gemini receives both OCR text and the original image. This improves accuracy on blurry or low-contrast receipts. It requires billing to be enabled on the Cloud project.
- Enable Cloud Document AI API in Google Cloud Console:
https://console.cloud.google.com/apis/library/documentai.googleapis.com - Create an Enterprise Document OCR processor in region
asia-southeast1. - Grant the function's runtime service account the Document AI API User role.
- Add the processor name to
functions/.env:DOCUMENT_AI_PROCESSOR_NAME=projects/PROJECT_ID/locations/asia-southeast1/processors/PROCESSOR_ID
Full walkthrough: docs/document-ai-ocr.md
Cost: ~$0.0015 per receipt (one page at $1.50 / 1 000 pages). If DOCUMENT_AI_PROCESSOR_NAME is not set, the function uses image analysis only.
The function can route analysis through either Google Gemini (default) or a
SiliconFlow-hosted vision model (e.g. Qwen/Qwen3-VL-8B-Instruct). Two env vars
control this — set them on the function (Cloud Run env vars or functions/.env):
RECEIPT_PROVIDER=siliconflow # "siliconflow" or "gemini" (default)
SILICONFLOW_MODEL=Qwen/Qwen3-VL-8B-InstructStore the SiliconFlow API key as a Firebase secret:
firebase functions:secrets:set SILICONFLOW_API_KEY
# Paste the sk-… value at the promptWhen RECEIPT_PROVIDER=siliconflow, the function calls SiliconFlow's
OpenAI-compatible chat/completions endpoint; Document AI OCR (step 6) still
runs first when configured. Full walkthrough: docs/siliconflow-setup.md
firebase deploy --only functionsflutter run -d chrome --dart-define=USE_MOCK_ANALYZER=falseTo point at a different backend during development:
flutter run -d chrome \
--dart-define=USE_MOCK_ANALYZER=false \
--dart-define=API_BASE=https://bill-splitt.web.appflutter build web --release --dart-define=USE_MOCK_ANALYZER=false
firebase deploy --only hostingImportant:
USE_MOCK_ANALYZER=falsemust be passed at build time. Without it, the mock analyzer is compiled in and no real API calls are made.
To deploy everything in one step:
flutter build web --release --dart-define=USE_MOCK_ANALYZER=false && firebase deployRun the Cloud Function locally against the deployed Firebase project:
cd functions
npm run serveThen in a second terminal:
flutter run -d chrome \
--dart-define=USE_MOCK_ANALYZER=false \
--dart-define=API_BASE=http://localhost:5001/bill-splitt/asia-southeast1flutter testCovers split math (compounded exclusive charges, discounts, rounding residual) and the debt-simplification algorithm (2 / 3 / 4-payer cases).
| Error | Cause | Fix |
|---|---|---|
429 RESOURCE_EXHAUSTED |
Free-tier Gemini rate limit (20 req / min) | Wait ~30 s; the function retries automatically if within the 60 s timeout. Upgrade to paid tier for higher throughput. |
403 PERMISSION_DENIED |
Gemini key restriction too tight | Confirm Generative Language API is allowed in key settings, or temporarily set restrictions to None. |
502 with Gemini returned non-JSON |
Malformed Gemini response | Use a clearer photo, or tap Reanalyze to run the OCR-assisted path again. |
Document AI PERMISSION_DENIED |
Runtime service account missing role | Grant Document AI API User to the function's service account in IAM. |
Document AI NOT_FOUND |
Wrong processor resource name | Confirm the full projects/…/locations/…/processors/… value in functions/.env. |
| App shows hardcoded mock receipt after deploy | USE_MOCK_ANALYZER=false not passed at build time |
Rebuild with --dart-define=USE_MOCK_ANALYZER=false. |
Check Cloud Function logs at any time:
firebase functions:log --only analyzeReceiptlib/
models/ Receipt, LineItem, Charge, Payer, SplitResult
services/
split_math.dart per-payer total calculation (compounded charges)
debt_simplifier.dart greedy creditor/debtor matching
receipt_api.dart FirebaseReceiptAnalyzer + MockReceiptAnalyzer
state/
bill_state.dart ChangeNotifier — receipt, payers, quantity grid, paid amounts
screens/ Home → Review → Summary
widgets/ PeopleSection, ItemCard, ChargesPanel, ReceiptImage, PayerAvatar
functions/
src/index.ts analyzeReceipt Cloud Function (Gemini or SiliconFlow + optional Document AI OCR)
docs/
gemini-setup.md Gemini API key setup walkthrough
siliconflow-setup.md SiliconFlow (Qwen3-VL) provider setup walkthrough
document-ai-ocr.md Document AI OCR setup walkthrough