Offline-first personal finance for Android. Income, expenses, transfers, budgets, dues and bank-SMS auto-capture — everything lives in a local SQLite database on your phone. Nothing is ever uploaded.
🌐 Website · ⬇️ Download APK · 🐛 Report a bug · ✨ Request a feature · 🏦 Add your bank
Most expense apps quietly lie to you. They blur where money sits with what it was for, count a transfer between your own accounts as income, or double-count a UPI payment because the bank sent two SMS. XPENC treats the ledger as the single source of truth and enforces one invariant everywhere:
Net worth = the sum of all account balances. A transfer leaves it unchanged, income raises it, expense lowers it. If the numbers ever disobey that, it's a bug.
Amounts are integer paise, never doubles — floats corrupt money math. Lending is not spending, and being repaid is not earning, so person movements are excluded from budgets and income/expense reports.
📐 The complete design doc — mental model, data model, every decision and the adversarial audit that hardened it — is in structure.md.
| 💳 Honest accounts | Cash / Bank / Card with real balances. Debit cards & UPI are linked instruments — they spend their bank's money, so rupees are never counted twice. Credit cards carry their own (negative = owed) balance. |
| 🔁 Income · Expense · Transfer | Three transaction types, kept strictly apart. Transfers never pollute budgets or reports. |
| 🎯 Budgets | Per-category with period windows, live progress, and once-per-period alerts at 80% and overspend. |
| 📩 Bank-SMS auto-capture | Bank messages parsed on-device into review cards (amount · account · merchant). Approve to post. Learned rules can Auto-Approve — exact matches only, with an Undo that really reverses the money. |
| 👥 Persons — dues & loans | They-owe / I-owe with running balances, partial settlements, optional real account movement. |
| 📅 Calendar & cash reminders | Day-wise in/out grid; EMI/bill reminders that post nothing until you confirm. |
| 📊 Insights | Category pie, income vs expense, net-worth trend, per-account reports — one chart engine, many views. |
| 💾 Backup & export | Symmetric JSON backup/restore + CSV shaped for accountants / Tally. |
| 🖤 Monochrome UI | Material 3, One UI–inspired, true-black AMOLED dark theme. |
Grab the latest APK from Releases or the website:
| Your phone | Asset |
|---|---|
| Most phones (~2017+) | xpenc-arm64-v8a.apk |
| Older 32-bit phones | xpenc-armeabi-v7a.apk |
| Emulators | xpenc-x86_64.apk |
Every release ships SHA256SUMS.txt — verify your download. APKs are built,
tested and gated by GitHub Actions; the
release process is fully automated from a version tag.
Bank SMS are read on-device, parsed on-device, and stored on-device. The app
requests READ_SMS and deliberately not RECEIVE_SMS: it scans the inbox
when you open it rather than running a background broadcast receiver. No message,
transaction or balance ever leaves the phone. There is no server.
See SECURITY.md for the vulnerability disclosure policy.
| Concern | Choice |
|---|---|
| Framework | Flutter 3.38.9 · Dart 3.10.8 |
| State | Riverpod |
| Database | Drift (SQLite), local-first, reactive queries |
| Routing | go_router |
| Charts | fl_chart |
| Notifications | flutter_local_notifications + timezone |
⚠️ Several packages are deliberately pinned (drift 2.31.0, riverpod 2.6.1, …). Do not bump them without reading the "Pinned dependencies" section of CONTRIBUTING.md — one of those bumps once shipped an APK that crashed on first open.
git clone https://github.com/PATILYASHH/XPENC.git
cd XPENC
flutter pub get
flutter runrm -rf build/native_assets # Windows only: Flutter native-assets bug
flutter analyze
flutter testThe widget tests render every screen against a real in-memory database at a real
phone size (360 × 800 dp) and fail on a layout overflow, so they catch what
flutter analyze cannot. test/branding_test.dart additionally fails the build
if AppInfo.version ever drifts from the version: line in pubspec.yaml.
flutter build apk --release --split-per-abi
bash tool/verify_apk.sh build/app/outputs/flutter-apk/app-arm64-v8a-release.apkverify_apk.sh must pass. It is the only thing standing between you and
shipping an APK with no libsqlite3.so — a failure no unit test can see,
because every test overrides the database with an in-memory one.
Database classes are generated from lib/data/tables.dart by drift. After
changing a table:
dart run build_runner build --force-jit --delete-conflicting-outputs--force-jit is required — without it build_runner fails with
'dart compile' does not support build hooks on this SDK. Schema-change
steps are in CONTRIBUTING.md.
Every asset — launcher icons, adaptive icon, Android 13 themed icon, splash marks, favicons and the website icons — is generated from one geometric definition:
python tool/generate_icons.pyNothing is hand-drawn, so the icons cannot drift out of sync. The in-app logo
(lib/core/branding/brand_mark.dart) redraws the same geometry as a
CustomPainter, so it stays sharp at any size and follows the theme.
lib/
core/ theme, Money type, branding, notifications
data/ drift database, tables, DAOs, seed data
domain/ entities, repository interfaces
features/ one folder per screen (dashboard, transactions, budgets, …)
website/ landing page (Vercel)
tool/ icon generator, APK verification gate
.github/ CI + release workflows, issue & PR templates
docs/ releasing / maintainer docs
Contributions are very welcome — the highest-impact one is adding an SMS template for your bank (config + regex, no app internals needed).
- 📖 CONTRIBUTING.md — setup, invariants, PR checklist
- 🤝 CODE_OF_CONDUCT.md
- 🔒 SECURITY.md — private vulnerability reporting
- 📦 CHANGELOG.md · docs/RELEASING.md — SemVer,
v*tags, automated releases
Yash Patil — GitHub @PATILYASHH · LinkedIn in/patilyasshh
MIT © 2026 Yash Patil