Mobile

Offline-first Flutter trip expense splitter

Flutter app for group trips with offline expense entry, sync on reconnect, multi-currency day-of rates, and minimum-transfer debt settlement.

flutterdartdriftsqliteriverpodfreezed

Built 1 time, downloaded 0 times.

Install from your terminal

Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.

npx claude-lab add offline-first-flutter-trip-expense-splitter-bccc70
10 filesDownload zip
CLAUDE.md
@REVIEW.md
@SPEC.md

# the app

## Purpose

Offline-first group trip expense splitter in Flutter. Solo developer, built for daily personal use. SPEC.md has the product and technical spec. PLAN.md has the phases.

## Commands

- `flutter pub get`
- `dart run build_runner build --delete-conflicting-outputs`
- `dart format --set-exit-if-changed .`
- `flutter analyze`
- `flutter test`
- `flutter test integration_test`
- `flutter run`

## Architecture

- Feature-first folders: `lib/features/{trips,expenses,settlement,sync,currency}`.
- Shared code lives in `lib/core` (db, money, clock).
- Riverpod for state.
- Drift (SQLite) is the single local source of truth.
- UI never calls the network directly. Go through a repository.

## Offline rule

- Every write goes to the local DB and an outbox table first.
- A sync worker drains the outbox when connectivity returns.
- The app must be fully usable in airplane mode.

## Money rule

- Never use `double` for money or rate math.
- Store amounts as integer minor units plus an ISO 4217 currency code.
- The `Money` value class in `lib/core/money` owns rounding and allocation.
- Allocation remainders follow the deterministic rule in SPEC.md.
- Currency decimals come from one table: JPY 0, USD/EUR 2, KWD 3.

## FX rule

- Each expense stores its own rate, rate date and rate source at creation time.
- Never recompute a stored rate later.
- Offline with no cached rate: the user enters a manual rate, flagged as `manual`.
- Rates are fetched through the `RateProvider` interface only.

## Sync rule

- Client-generated UUIDv7 ids.
- Every record has `updatedAt` and a device id.
- Deletes are soft (tombstones).
- Every sync operation must be idempotent. Duplicate delivery must be harmless.
- Conflict policy: last-writer-wins per field. Details live in SPEC.md.

## Settlement rule

- Compute net balance per person per trip base currency.
- Then apply greedy min-cash-flow matching.
- Settlement code is a pure Dart function in `lib/features/settlement`.
- No Flutter imports in settlement code.
- Balances must sum to exactly zero after rounding. The remainder goes to a deterministic member.

## Time rule

- Inject a `Clock`. Never call `DateTime.now()` directly.
- Store timestamps in UTC.
- An expense date is the user's local calendar date at the trip location.

## Database and generated code

- After changing Drift tables or freezed classes, run build_runner.
- Schema changes need a migration and a migration test.
- Never hand-edit `*.g.dart` or `*.freezed.dart`.
- Whether generated files are committed or ignored follows the decision in SPEC.md.

## Testing

- Unit tests for money, FX and settlement, with property-style randomized cases.
- Use a fake sync backend (`FakeSyncRemote`) for offline and online transitions.
- No real network calls in tests.
- Integration tests must pass with the network off.

## Dependencies

- Ask before adding any package beyond: drift, drift_dev, build_runner, flutter_riverpod, freezed, freezed_annotation, uuid, sqlite3_flutter_libs.
- Justify every new dependency in the PR or commit message.

## Secrets

- Never commit secrets or real keys.
- Backend keys use `YOUR_API_KEY` placeholders in examples.
- Real values come from `--dart-define` or a gitignored env file.

## Workflow

- Explore, then plan, then code, then verify.
- Run the `verify` skill before saying a task is done.
- Use `/clear` between unrelated tasks and `/compact` at natural breaks.
- Use `test-runner` for the full suite to keep logs out of the main context.
Use for my project