Skip to content

preetanshumishra/PatternKitAndroidMviCleanCompose

Repository files navigation

PatternKit — MVI + Clean Architecture (Jetpack Compose)

Part of PatternKit, a side-by-side reference codebase where the same small Tasks CRUD app is implemented once per architecture pattern across iOS and Android. Every module ships identical behaviour — the same domain model, the same three screens, the same mock data layer — so the only thing that varies is the architecture itself.

This module is the MVI (Model–View–Intent) + Clean Architecture flavour on Jetpack Compose. Each screen is driven by unidirectional data flow: the UI emits intents, the ViewModel reduces them into a single immutable state object, and Compose renders that state. Like the Clean module it keeps a domain layer of use cases; the difference is the strict one-way state/intent contract in the UI layer — the approach favoured for complex, state-heavy screens (Cash App's Molecule is the canonical industry example).

Stack

  • Language: Kotlin
  • UI: Jetpack Compose + Material 3
  • Architecture: MVI + Clean (unidirectional data flow over Data / Domain / UI)
  • DI: Dagger 2 (KSP) — plain Dagger, no Hilt
  • Navigation: Navigation Compose
  • Min SDK: 28 · Target/Compile SDK: 36
  • Package: com.preetanshumishra.patternkit.android.mvicleancompose

The Tasks feature

A single-user task list. One entity (TaskItem: title, optional notes, optional due date, priority, completion). Three screens:

  1. List — filter chips (All / Active / Completed), sort by due date or priority, swipe-to-delete, FAB to create.
  2. Detail — read-only fields, toggle completion, edit, delete.
  3. Form — create or edit (mode-driven), title validation (≤ 80 chars), due-date validation (not in the past), 600 ms mock async save.

Data comes from MockTaskRepository — an in-memory store seeded with ~12 tasks, with configurable artificial latency and failure rate. No real network, no local persistence — intentionally, so the architecture stays the focus.

How MVI shapes the UI layer

Each ViewModel exposes a single immutable state and accepts intents (user actions) as input. State transitions happen in one place, making every screen update explicit and replayable. Beneath the UI, the same five Clean use cases drive the data flow:

  • GetTasksUseCase, CreateTaskUseCase, UpdateTaskUseCase, DeleteTaskUseCase, ToggleTaskCompletionUseCase

Dependency injection

Plain Dagger 2, wired by hand:

  • AppComponent (@Singleton) exposes the five use cases; RepositoryModule @Binds TaskRepositoryMockTaskRepository.
  • Use cases and the repository are constructor-injected, so Dagger builds them with no @Provides boilerplate.
  • PatternKitApp creates and holds the component; a hand-rolled ViewModelFactory constructs each ViewModel from the use cases in the graph.

Project layout

app/src/main/kotlin/.../mvi/
├── data/        # MockTaskRepository, seed data
├── domain/      # TaskItem, Priority, TaskRepository (contract), UseCases
├── di/          # AppComponent, RepositoryModule
├── ui/
│   ├── list/    # TaskListScreen + TaskListViewModel (state + intents)
│   ├── detail/  # TaskDetailScreen
│   ├── form/    # TaskFormScreen + TaskFormViewModel
│   ├── nav/     # NavHost + Routes
│   ├── theme/   # Material 3 theme
│   └── ViewModelFactory.kt
└── PatternKitApp.kt

Build & run

./gradlew assembleDebug      # build the debug APK
./gradlew installDebug       # install on a connected device/emulator
./gradlew test               # unit tests

Or open the project in Android Studio and run the app configuration.

About

Tasks CRUD reference app built with Jetpack Compose, MVI and Clean Architecture (unidirectional state, pure reducers). Part of PatternKit, a side-by-side comparison of mobile architecture patterns.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages