Skip to content

preetanshumishra/PatternKitAndroidMviCleanXml

Repository files navigation

PatternKit — MVI + Clean Architecture (XML Views)

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 + Clean Architecture flavour on the traditional XML view stack — Fragments, the Navigation component, ViewBinding, and a RecyclerView adapter. Each feature is an MVI store: the View sends Intents, a pure reducer derives the next immutable State, and one-shot Effects (navigation) are delivered over a Channel. It's the XML counterpart to the MVI-Clean-Compose module — identical store, reducers, and contract; only the View layer differs (Fragments collect state/effects flows instead of Compose).

Stack

  • Language: Kotlin
  • UI: XML layouts + ViewBinding · Fragments · RecyclerView
  • Architecture: MVI (Intent / State / Result / Effect + pure reducer) over Clean (Domain / Data / UI)
  • State: StateFlow collected with repeatOnLifecycle; one-shot effects over a Channel
  • Navigation: Jetpack Navigation component (nav_graph.xml), driven by MVI Effects
  • DI: Dagger 2 (KSP) — plain Dagger, no Hilt
  • Min SDK: 28 · Target/Compile SDK: 36
  • Package: com.preetanshumishra.patternkit.android.mvicleanxml

The Tasks feature

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

  1. List (TaskListFragment) — filter chips (All / Active / Completed), sort by due date or priority, swipe-to-delete via RecyclerView, FAB to create.
  2. Detail (TaskDetailFragment) — read-only fields, toggle completion, edit, delete.
  3. Form (TaskFormFragment) — 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.

The MVI shape (per feature)

  • Intent — everything the UI can ask for (TaskListIntent, TaskFormIntent); the View's single entry point is onIntent(...).
  • Result — the outcome of handling an intent, fed to the reducer.
  • State — one immutable …UiState exposed as a StateFlow; the Fragment collects it with repeatOnLifecycle(STARTED) and renders.
  • Effect — one-shot side effects the UI can't model as state (navigation), delivered over a Channel and collected by the Fragment. The View never decides a transition itself — e.g. tapping a row sends TaskClicked, the store emits NavigateToDetail.
  • ReducerreduceTaskList / reduceTaskForm are pure top-level functions (no coroutines, no I/O), so state transitions are synchronously unit-testable.

The detail and form screens feed edits back into the shared list store as intents (TaskToggled, TaskDeleted, TaskSaved) rather than mutating it directly.

The domain layer (the "Clean" part)

Five single-responsibility use cases sit between the stores and the data layer (UseCases.kt): GetTasksUseCase, CreateTaskUseCase, UpdateTaskUseCase, DeleteTaskUseCase, ToggleTaskCompletionUseCase. Each depends only on the TaskRepository contract (in domain/). Stores translate intents into results by running use cases — they never touch the repository directly.

Dependency injection

Plain Dagger 2, wired by hand. AppComponent (@Singleton) exposes the five use cases; RepositoryModule @Binds TaskRepositoryMockTaskRepository. PatternKitApp holds the component; a hand-rolled ViewModelFactory pulls the use cases out of the graph and constructs each store.

Project layout

app/src/main/
├── kotlin/.../mvicleanxml/
│   ├── domain/      # TaskItem, Priority, TaskFilter, TaskSort,
│   │               #   TaskRepository (contract), UseCases.kt
│   ├── data/        # MockTaskRepository, seed data
│   ├── di/          # AppComponent, RepositoryModule
│   ├── ui/
│   │   ├── list/    # TaskListFragment + TaskAdapter + TaskListViewModel (store + reducer)
│   │   ├── detail/  # TaskDetailFragment
│   │   ├── form/    # TaskFormFragment + TaskFormViewModel (store + reducer)
│   │   ├── NavArgs.kt, MainActivity, ViewModelFactory
│   └── PatternKitApp.kt
└── res/
    ├── layout/      # activity_main, fragment_list/detail/form, item_task
    ├── menu/        # menu_list, menu_detail
    └── navigation/  # nav_graph.xml

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 Android XML Views, 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