Personal finance tracker. Flutter + Firebase. Track spending, set budgets, hit savings goals, scan receipts with on-device AI.
- Log income + expenses with category, notes, date, recurring schedule
- Full transaction list — live search (note/amount/category), filters (type/category/date range)
- Edit + delete with optimistic updates
- Floating add button on activity page
- 31 built-in vector icons (food, transport, bills, etc.)
- Custom categories with icon + color picker
- Income/expense classification per category
- Default categories editable + deletable
- Inline "+ Add category" tile in transaction picker — no navigation needed
- Backward-compat with legacy emoji-based data
- Budgets — monthly limits per category, progress rings, near-limit + over-limit alerts
- Savings goals — target amount, deadline, contribute flow, ring color shifts red → amber → green
- Combined "Plan" tab with Budgets + Savings sub-tabs
- Camera capture → on-device OCR → AI parse → review screen → save as transaction
- Two-stage pipeline:
- Google ML Kit — text recognition runs fully on-device (Apple Vision on iOS, Google ML on Android)
- Groq LLM API — parses raw OCR text into structured JSON (merchant, amount, date, items, category guess)
- Review card shows extracted fields, lets user edit before save
- Picks expense category by default (Salary excluded from picker)
- Weekend vs weekday spend pattern
- Category drift detection (50%+ MoM increase)
- Burn-rate projection (will you blow budget at current pace?)
- Unusual transaction flag (>2σ from 30-day mean)
- Savings streak achievements
- Dismissible + restorable
- Min data threshold (7 txns across 3 days) before generating
- Overall spend ring + budget heatmap
- Active goal chips → tap to contribute
- Recent transactions feed
- Notification bell — combined badge for budget alerts + active insights
- Notification sheet with dismiss/restore for insights
- Email + password
- Google Sign-In (OAuth)
- Persistent session via Firebase Auth state stream
- GoRouter redirect guards
- Glass/frosted floating pill nav (BackdropFilter blur + transparent fill)
- Editorial typography — Instrument Serif headings, JetBrains Mono for currency, system font body
- Warm beige + ink palette
- Offline banner on disconnect
- Keyboard auto-dismiss on tap-outside
- Smooth modal sheets with safe-area handling
- Dynamic safe-area override propagates pill clearance to all scroll views
Clean architecture + feature-first folder layout.
lib/
├── core/ # Theme, routing, utils, DI
│ ├── routing/ # GoRouter + ShellRoute + MainShell (pill nav)
│ ├── theme/ # AppColors, AppTypography
│ └── utils/ # CurrencyFormatter, CategoryIcon, DateUtils
├── features/ # One folder per domain
│ ├── auth/
│ ├── transactions/
│ ├── categories/
│ ├── budgets/
│ ├── goals/
│ ├── analytics/
│ ├── dashboard/
│ ├── insights/
│ └── scan/ # Receipt OCR + AI parse
└── shared/widgets/ # Reusable: EmptyState, LoadingIndicator, etc.
Per-feature layers:
- domain/ — models, business rules (pure Dart, no Flutter)
- data/ — repositories, Firestore queries
- presentation/ — BLoC (event/state/bloc) + pages + widgets
Dependencies flow inward: presentation → data → domain.
flutter_bloc (BLoC pattern). One bloc per domain:
| Bloc | Responsibility |
|---|---|
| AuthBloc | Sign-in/out, listens to authStateChanges() |
| TransactionBloc | CRUD, filter, search |
| CategoryBloc | Category CRUD, default seeding |
| BudgetBloc | Budget limits, calc spent/remaining |
| GoalBloc | Goal CRUD, contributions, completion |
| InsightBloc | Rule engine, dismiss/restore |
Events → bloc → state (Loading | Loaded | Error). UI rebuilds via BlocBuilder. Side-effects via BlocListener. BlocProvider.value propagates blocs into modal sheets.
| Layer | Tech |
|---|---|
| UI | Flutter (Material 3) |
| State | flutter_bloc, equatable |
| Routing | go_router (ShellRoute) |
| Backend | Firebase Auth + Cloud Firestore |
| Auth | firebase_auth + google_sign_in |
| DI | get_it |
| Charts | fl_chart |
| Icons | lucide_icons + Material Icons |
| Fonts | google_fonts (Instrument Serif, JetBrains Mono) |
| OCR | google_mlkit_text_recognition (on-device) |
| LLM | Groq API (HTTP) |
| Storage | shared_preferences (prefs), Firestore (data) |
| Network | connectivity_plus, http |
| Env | flutter_dotenv |
Firestore per-user collections:
users/{uid}/
├── transactions/{id} # amount, type, categoryId, note, date, recurring
├── categories/{id} # name, icon, color, isIncome, monthlyLimit?
├── goals/{id} # title, targetAmount, currentAmount, deadline, status
└── prefs/... # dismissed insight IDs, etc.
Budgets are derived from category monthlyLimit + transaction aggregation — no separate budget collection.
Firestore offline persistence enabled by default → cached reads, queued writes, syncs on reconnect.
- Flutter 3.19+
- Firebase project (Auth + Firestore enabled)
- iOS: Xcode 15+, CocoaPods
- Android: Android Studio, JDK 17
- Groq API key (free tier OK)
git clone https://github.com/KartavyaY/SpendSnap.git
cd SpendSnap/finance_app
flutter pub getlib/firebase_options.dart is gitignored. Generate:
dart pub global activate flutterfire_cli
flutterfire configureCreate .env in project root:
GROQ_API_KEY=your_key_here
Loaded at startup via flutter_dotenv.
Project starts in Firebase "Testing" mode — add your Gmail under Google Cloud Console → APIs & Services → OAuth consent screen → Test users. Publish flow requires Google review.
flutter run- iOS (
Info.plist):NSCameraUsageDescription,NSPhotoLibraryUsageDescription - Android (
AndroidManifest.xml):CAMERA,INTERNET
- Receipt OCR runs fully on-device — image never leaves the phone for text extraction
- OCR text (not image) sent to Groq for structured parse
- Firestore writes go to user's own document tree (
users/{uid}/...) - Auth tokens managed by Firebase SDK
- No analytics or third-party tracking
- Email + Google auth
- Transaction CRUD with categories + recurring
- Search + filter activity
- Budget limits + alerts
- Savings goals with rings
- Custom categories with income/expense classification
- Rule-based insights with dismiss/restore
- Notification panel
- Lucide icon system
- Receipt scanning (camera → ML Kit OCR → Groq LLM → transaction)
- Glass pill nav with BackdropFilter
- Multi-currency
Personal project. Not open-sourced.