ExpenseShare is a Compose Multiplatform (CMP) application designed for seamless group expense sharing.
Users can create groups, add friends, record transactions, and the system automatically calculates how much each member owes or is owed.
All data is synced through the backend — the app currently uses no local database.
AI-powered features will be added later to assist with categorization, analysis, and smart recommendations.
This repository includes both the CMP client and the Ktor backend.
Here is a glimpse of ExpenseShare in action across different platforms:
| Group List | Two pane view (Canonical layouts) | List of transaction | Add Transaction | Desktop |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
- Create and manage groups
- Add members
- View group activity and balances
- Add and manage friends
- Create groups using your friend list
- Friend relationships synced with the backend
- Add expenses
- Select payer
- Split cost among group members
- All members can add new transactions
- Real-time debt/credit calculation
- Clear overview of who owes whom
- Group updates propagate to all members
- AI-based transaction classification
- Smart settlement suggestions
- Expense pattern insights
Supported platforms:
- Android
- iOS
- Desktop (JVM: Windows, macOS, Linux)
- Web (WASM)
- Clean Architecture
- MVI
- Compose Multiplatform
- Koin
- Coroutines + Flow
- No local DB (server = source of truth)
The app uses a hierarchical navigation structure:
graph TD
Root[RootRoute] --> Auth[AuthRoute]
Root --> Main[MainRoute]
subgraph AuthFlow [Authentication Flow]
Auth --> Login[Login Screen]
Auth --> Register[Register Screen]
end
subgraph MainFlow [Main Application Flow]
Main --> Dashboard[Dashboard Screen]
Main --> Friends[Friends Screen]
Main --> Profile[Profile Screen]
Dashboard --> GroupDetail[Group Detail View]
Dashboard --> AddTransaction[Add Transaction View]
Friends --> FriendDetail[Friend Detail View]
end
- RootRoute: Handles switching between
AuthandMainflows.- AuthRoute: Contains
LoginandRegisterscreens. - MainRoute: Contains
Dashboard,Friends, andProfilescreens.
- AuthRoute: Contains
The project follows the MVI (Model-View-Intent) pattern using a BaseViewModel.
- State: Managed via
StateFlow. The UI observesviewStateto react to changes. - Actions: The UI sends
Actions(e.g.,LoadData,SelectGroup) to the ViewModel via thehandle()function. - Events: One-time side effects (e.g., showing a Toast, navigating) are emitted via
SharedFlow.
To ensure a smooth and responsive user experience, the application maintains the latest data in memory within the ViewModel state:
-
Reactive Updates: When a transaction is added or a group is updated, the
ViewModelupdates its internalgroupslist by applying the changes to the existing state. -
Single Source of Truth (Client-side): While the server is the ultimate source of truth, the
ViewModelstate acts as a local cache. When an API call succeeds, theViewModelsurgically updates the specific entity (e.g., adding aTransactionto aGroup) in thegroupslist. -
Seamless UI: This approach allows the UI to reflect changes immediately without requiring a full screen reload or a new fetch from the network, provided the local state is correctly synchronized with the server's response.
-
commonMain → Shared UI & domain logic
-
androidMain → Android-specific
-
iosMain → iOS integration
-
jvmMain → Desktop
-
wasmJsMain → Web (WASM)
The backend manages all persistence & logic:
- Ktor Server
- PostgreSQL
- Exposed ORM
- Authentication
- Groups, Friends, Transactions APIs
- Settlement calculation
Folder:
/server
./gradlew jvmRun -DmainClass=org.milad.expense_share.MainKt --quiet
./gradlew :server:run
./gradlew kotest
./gradlew :composeApp:wasmJsBrowserDevelopmentRun
This project demonstrates:
- Production-grade Compose Multiplatform
- Clean modular design
- Full backend with Ktor + PostgreSQL
- Multi-platform targeting (Android/iOS/Web/Desktop)
- Extendable architecture for upcoming AI features
A strong showcase of modern Kotlin ecosystem practices.




