Skip to content

About

Flutter client for EquiSplit — expense splitting and group balance management with Firebase, offline caching and live balance updates.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

108 Commits

Folders and files

Repository files navigation

EquiSplit — Mobile & Web Client

A cross-platform expense splitting and group finance app built with Flutter. Split bills, track who owes whom, and settle up on Android, iOS, Web, macOS and Windows from a single codebase.

EquiSplit is serverless: the app talks directly to Firebase (Auth, Firestore, Storage, Cloud Functions, FCM). There is no REST API and no Laravel backend in the request path. The retired Laravel implementation lives in the sibling equisplit repository for historical reference only.


✨ Features

Groups & people

  • Create groups for trips, roommates, events or households
  • Invite members with a shareable /join?token= link and claim it on a second account
  • Member directory with avatars, contact details and per-group roles
  • Cash-inflow tracking — money coming in (salary, refunds) linked to specific expenses

Expenses & splitting

  • Add expenses with amount, category, date and payer
  • Split across any subset of members — equally or by custom shares
  • Automatic balance calculation: who owes whom, minimised into a settlement list
  • Settlement tracking, so a recorded payment clears the debt

Analytics

  • Spending statistics and trends per group, rendered with FL Chart
  • Category breakdowns and cash-flow charts

Platform & real-time

  • Live updates from Firestore snapshots — balances and expenses appear without a refresh
  • Push notifications via FCM, including settlement reminders and expense alerts
  • Offline-first — Firestore caches reads and queues writes until connectivity returns
  • Google sign-in, email/password auth, avatar uploads to Firebase Storage
  • Slidable expense rows, emoji picker, currency picker, in-app ads on mobile

🚀 Platform support

Platform Status Notes
🌐 Web ✅ Firebase JS SDK 13.1.0; web push via service worker
🍎 macOS ✅ Native macOS application
🪟 Windows ✅ Native Windows application (no local notifications)
🤖 Android ✅ minSdk 24
📱 iOS ✅ iOS 13.0+

Linux is intentionally unsupported: FlutterFire has no Linux implementation for this stack.

🔧 Current status

  • Firebase project: equisplit-d4c74 ✅ configured
  • Firestore database: europe-west1 ✅ deployed (recreated from nam5)
  • Hosting: https://equisplit-d4c74.web.app ✅ deployed and live
  • Cloud Functions: ⏳ pending (requires Cloud Functions API enablement)
  • Storage: ✅ configured (bucket reference: equisplit-d4c74.firebasestorage.app)

Note: The Cloud Functions API is currently disabled and requires console enablement. Rules, indexes, and hosting are deployed and live.

🔍 Quick verification

Check the live site:

curl -I https://equisplit-d4c74.web.app
# Should return HTTP/2 200

🏗️ Architecture

Feature-first structure with a shared layer:

lib/
├── main.dart                       # Entry point (Firebase bootstrap, sign-in init, ads)
├── firebase_options.dart           # Generated by flutterfire configure
└── src/
    ├── app.dart                    # App shell & theme
    ├── core/
    │   ├── constants/              # Default category list
    │   └── firebase/
    │       ├── firebase_bootstrap.dart   # init + emulators + App Check
    │       ├── firestore_refs.dart       # typed collection/document refs
    │       ├── firestore_converters.dart # Timestamp/num/string converters
    │       └── firebase_error_mapper.dart
    ├── features/
    │   ├── auth/                   # Firebase Auth, profile, invites, avatar
    │   ├── home/                   # Dashboard & personal balance
    │   ├── groups/                 # Group list, detail, invites
    │   ├── expenses/               # Expenses, splits, settlement, balances
    │   ├── categories/             # Category management
    │   ├── cash_inflows/           # Income / cash-inflow tracking
    │   ├── people/                 # Member directory
    │   └── analytics/              # Statistics & charts
    └── shared/
        ├── services/               # FCM/local notifications
        ├── presentation/widgets/   # Reusable UI widgets
        └── utils/                  # Formatters, platform detection, helpers

functions/                          # Cloud Functions (TypeScript, Node 20)
├── src/auth.ts                     # bootstrapUser, syncMemberProfile
├── src/groups.ts                   # invites, members, delete, reset
├── src/expenses.ts                 # onExpenseCreated push
├── src/notifications.ts            # reminders, test push
├── src/devices.ts                  # hasFcmToken mirrors
├── src/cleanup.ts                  # scheduled invites + onUserDelete
└── src/lib/                        # Admin SDK helpers, FCM, constants

firestore.rules                     # Security rules
firestore.indexes.json              # Composite + collection-group indexes
storage.rules                       # Avatar-only bucket rules
firebase.json                       # Rules/indexes/storage/functions/emulators

State management: Riverpod. Repositories expose Streams over Firestore, and providers are scoped per feature.

Balance math: BalanceCalculator is pure Dart and runs in the client (features/expenses/domain). There is no users.balance field; the personal balance is derived as cash inflows − personal-group expenses.

Routing: Navigator + MaterialApp.home; invite links are read from Uri.base.queryParameters['token'] in AuthGate.


📦 Tech stack

Concern Choice
Framework Flutter 3.47 / Dart 3.13+
State management Riverpod
Backend Firebase Auth, Firestore, Storage, Cloud Functions, FCM
Auth firebase_auth, google_sign_in 7
Push firebase_messaging 17 + flutter_local_notifications
Security Firestore/Storage rules, optional App Check
Charts FL Chart
UI Google Fonts, flutter_slidable, emoji_picker_flutter, currency_picker
Ads google_mobile_ads (Android/iOS only)
Media image_picker

🛠️ Getting started

Prerequisites

  • Flutter SDK 3.47 or higher
  • Dart 3.13 or higher
  • Node.js 20 and firebase-tools (npm i -g firebase-tools) for Functions and emulators
  • JDK 21+ — firebase-tools refuses to start the emulators on older runtimes
  • Platform toolchains: Xcode + CocoaPods (macOS/iOS), Android Studio/SDK, Visual Studio 2022 (Windows)

The Firebase project is equisplit-d4c74. Its client config is already committed in lib/firebase_options.dart, android/app/google-services.json, and the Apple plists.

1. Clone & install

git clone https://github.com/zuhaibranaa/expense_split.git
cd expense_split

flutter pub get
npm --prefix functions install

2. Run against the Firebase emulators

Start the emulator suite (Auth, Firestore, Storage, Functions, UI):

firebase emulators:start

Then launch the app with the emulator flag:

flutter run -d chrome --dart-define=USE_FIREBASE_EMULATORS=true

The Android emulator automatically uses 10.0.2.2 for the host; every other target uses localhost.

3. Run against the real project

flutter devices

flutter run -d chrome
flutter run -d macos
flutter run -d windows
flutter run -d <android-or-ios-device>

4. Deploy rules, indexes and hosting

Already deployed in this session:

# Rules, indexes, and hosting are already deployed:
firebase deploy --only firestore:rules,firestore:indexes,storage
firebase deploy --only hosting

Current status:

  • ✅ Firestore rules and indexes deployed to europe-west1
  • ✅ Hosting live at https://equisplit-d4c74.web.app
  • ⏳ Cloud Functions pending (requires Cloud Functions API enablement)

To enable Cloud Functions API:

  1. Go to Firebase Console
  2. Enable the Cloud Functions API
  3. Set up Blaze billing if needed
  4. Then run:
npm --prefix functions run build
firebase deploy --only functions

Functions deploy to europe-west1 (see functions/src/index.ts). Set the invite link host if you use a custom domain:

# functions/.env
INVITE_BASE_URL=https://app.example.com

Without functions/.env, invite links default to https://equisplit-d4c74.web.app.

Optional: App Check

Client wiring is compiled out unless you pass:

flutter run -d chrome --dart-define=ENABLE_APP_CHECK=true

Do not enforce App Check in Firebase until parity QA passes.


🏗️ Building for production

flutter build web     --release   # build/web/
flutter build apk     --release   # build/app/outputs/flutter-apk/app-release.apk
flutter build appbundle --release # build/app/outputs/bundle/release/
flutter build ipa     --release   # requires Xcode signing
flutter build macos   --release
flutter build windows --release

For web hosting on Firebase:

flutter build web --release
firebase deploy --only hosting

Note: The Flutter web build and Firebase Hosting deployment has already been completed in this session. The site is currently live at https://equisplit-d4c74.web.app.


🧪 Quality

flutter analyze                    # static analysis
flutter test                       # 33 unit tests (converters, models, balances, errors)
dart format lib test
npm --prefix functions run build   # type-check Cloud Functions
npm --prefix functions run lint

Currently deployed:

✅ Rules & Indexes: Deployed to production Firestore (europe-west1) ✅ Hosting: Live at https://equisplit-d4c74.web.app ⏳ Cloud Functions: Pending (requires Cloud Functions API enablement)

Emulator testing (for development):

# Firestore + Storage rules (36 tests, every deny path)
firebase emulators:exec --only firestore,storage \
  "npm --prefix functions test"

# Golden path: signup → personal group → invite → claim → expense → reminders
#              → reset → remove member → delete group
firebase emulators:exec --only auth,firestore,functions \
  "npm --prefix functions run test:integration"

The golden-path test talks to the emulator REST APIs directly, so the real rules and callables are exercised without the Flutter client.


🔍 Troubleshooting

Web

  • auth/unauthorized-domain → add the origin under Firebase Auth → Settings → Authorized domains
  • Push does nothing → confirm web/firebase-messaging-sw.js is served from the site root and the VAPID key matches NotificationService._vapidKey

macOS / iOS

  • CocoaPods errors → pod install inside the platform directory
  • Google sign-in fails → check the REVERSED_CLIENT_ID URL scheme is in Info.plist

Android

  • Emulator can't reach emulators → 10.0.2.2 is handled automatically by firebase_bootstrap.dart; verify the emulator is started with firebase emulators:start
  • google-services.json missing → re-run flutterfire configure --project=equisplit-d4c74

Functions (pending)

  • permission-denied calling a function → send a CORS-enabled call from the app (callables do this) and check the user is signed in
  • internal errors → firebase functions:log

Current status

  • Hosting is deployed and live: https://equisplit-d4c74.web.app
  • Cloud Functions API requires console enablement (not yet deployed)

General

  • Build errors → flutter clean && flutter pub get
  • Platform unavailable → flutter doctor

🔗 Related

📄 License

MIT

About

Flutter client for EquiSplit — expense splitting and group balance management with Firebase, offline caching and live balance updates.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages