Receipt Splitter
A Flutter mobile application for scanning receipts, creating shared bill drafts, splitting expenses between participants, and tracking open settlements using local Drift persistence.

The Problem
Splitting receipts between multiple people becomes messy when a bill contains several items, different payers, partial payments, and changing participants. A simple receipt scanner is not enough; the application also needs to turn scanned items into a bill that can be edited, split, finalized, and tracked until everyone has paid.
- Receipt items should not have to be entered manually one by one.
- A bill can contain multiple expenses, participants, payers, and debt splits.
- Users need to know which bills are still open and who still owes money.
- Bill, expense, participant, and payment data should remain available locally on the device.
The Solution
Receipt Splitter combines OCR-assisted receipt scanning with a local shared-expense workflow. Users can scan or import a receipt image, crop it for better recognition, convert detected receipt lines into draft expenses, add participants, assign payers, split costs, finalize bills, and track outstanding payments from a dashboard.
- Scan receipt images using the device camera or gallery.
- Convert OCR output into structured receipt items.
- Create editable bill drafts from detected receipt items.
- Add participants and split each expense between selected people.
- Track draft, open, and settled bills.
- View dashboard summaries for open settlements, biggest bills, and largest debtors.
Architecture
Local-First Mobile Application Flow
The application uses a layered Flutter architecture. The UI layer is split into pages, widgets, and ViewModels. ViewModels expose screen state and delegate persistence to a repository. The repository uses Drift tables and mappers to translate database rows into UI domain models. OCR follows a separate pipeline where an image is picked or captured, cropped, processed by ML Kit text recognition, parsed into receipt items, and inserted as draft expenses in the local database.
static List<ReceiptItem> parse(String rawText) {
final lines = rawText
.split('\n')
.map(_normalizeLine)
.where((line) => line.isNotEmpty)
.toList();
final separatedItems = _parseSeparatedItemsAndPrices(lines);
if (separatedItems.isNotEmpty) {
return separatedItems;
}
final inlineItems = _parseInlineItems(lines);
return inlineItems;
}
Screenshots





Technical Analysis
OCR As An Input Pipeline
The OCR feature is not treated as a standalone demo. It feeds directly into the bill workflow by converting recognized receipt text into structured receipt items and creating a bill draft from the result.
Local-First Persistence With Drift
Bills, participants, persons, expenses, and expense splits are stored locally using Drift. This makes the application usable without a backend while still keeping the data model relational and structured.
Repository And Mapper Layer
Database access is kept behind a BillRepository. Drift rows are converted into UI models through mapper classes, which prevents screens from depending directly on database tables.
ViewModel-Driven Screens
Screens use Provider-backed ViewModels to handle state, subscriptions, validation, and user actions. This keeps UI widgets focused on rendering instead of containing bill and payment logic.
Derived Settlement Data
Dashboard statistics such as open settlement value, top bills, and largest debtors are calculated from bill, expense, and split data instead of being stored separately. This reduces duplicated state and keeps summaries tied to the source data.
Bill Lifecycle Validation
A bill starts as a draft, can only be opened after expenses have a payer and valid splits, and becomes settled when all expense splits are fully paid. This gives the application a clear workflow instead of treating bills as simple CRUD records.
Challenges
The main challenge was connecting OCR input to a real expense-splitting workflow. The application has to deal with unreliable OCR text, editable receipt items, participants, payers, split validation, draft bills, open bills, and settlement status. The technical difficulty is not one algorithm, but keeping those concerns separated while still making them work as one mobile flow.
What This Project Demonstrates
Mobile Application Architecture
The project separates screens, reusable widgets, ViewModels, services, repositories, mappers, database tables, and domain models.
Local Data Modeling
The Drift database models bills, people, participants, expenses, and expense splits as separate relational concepts instead of storing everything as one flat object.
OCR Integration
The application integrates camera/gallery selection, image cropping, ML Kit text recognition, receipt parsing, and bill creation into one workflow.
Business Logic Outside The UI
Payment validation, debt aggregation, settlement status updates, and receipt parsing are handled outside presentation widgets, making the application easier to reason about and extend.

A Bit About Me
Outside of software development, I enjoy gaming, anime, and working on personal projects that help me learn something new.
Whether it's building applications, automating workflows, or experimenting with new technologies, I enjoy creating things and understanding how they work.