An offline-first list-making app for Android and iOS, built as a teaching reference. It uses Kotlin Multiplatform to share domain, data, and presentation state while keeping the UI native with Jetpack Compose and SwiftUI.
The project is a worked example of modularization, dependency boundaries, native UI over a shared state layer, code generation, and architecture enforcement.
The patterns here are drawn from FluxIt, a commercial app I'm building. This repository is a teaching reference, not the product — it implements a subset of the app's functionality and is deliberately structured to demonstrate more than the product needs.
It is deliberately over-engineered, to demonstrate patterns you'd only reach for at scale. A four-feature list app does not need sixteen Gradle modules, generated design tokens, or Konsist architecture tests. The point is to show those mechanisms working end to end on a codebase small enough to read in an afternoon — not to suggest this is the right amount of structure for an app this size.
Source code is licensed under Apache-2.0; the FluxIt brand assets and the
reference mockups in design/ are reserved. See
LICENSE and LICENSE-ASSETS.
The core list-management experience is implemented on both platforms:
- Lists dashboard with search, create, edit, delete, and undo flows
- List detail with item creation, completion, deletion, and progress
- Item detail with notes and photo capture or selection
- Native Android and iOS navigation over shared MVI stores
- Local persistence with SQLDelight
- Platform logging, analytics, configuration, reminders, and photo capabilities
Calendar and Starred remain placeholder tabs. Authentication, networking, and multi-device sync are outside the current offline-first scope. Future work should be prioritized from product needs rather than an inherited implementation plan.
- JDK 21 (Temurin recommended), pinned in
mise.toml - Android SDK platform 35 and build-tools 35.0.0
- Xcode 16+ with an iOS 16+ Simulator runtime
- Ruby 3.4.9, pinned in
mise.toml xcodegen,openssl,libyaml, andreadlinefrom Homebrew for iOS setup
Gradle is supplied by the wrapper and does not need to be installed separately.
-
Create a gitignored
local.propertiesat the repository root:sdk.dir=/Users/<you>/Library/Android/sdk -
Build the debug APK:
./gradlew :android-app:assembleDebug
-
Install it on a running emulator or connected device:
./gradlew :android-app:installDebug
You can also open the repository in Android Studio and run android-app.
-
Install the native build dependencies:
brew install xcodegen openssl libyaml readline mise install
-
Generate the Xcode project, build the shared XCFramework, and run the iOS smoke build:
scripts/build-ios.sh
-
Open
ios-app/FluxIt.xcodeproj, select theFluxItscheme, and run it on an iOS 16+ Simulator.
ios-app/project.yml is the source of truth for the generated Xcode project.
android-app/ Compose application and Android navigation
ios-app/ SwiftUI application consuming the shared XCFramework
shared/domain/ Entities, use cases, repository and platform contracts
shared/data/ SQLDelight database and repository implementations
shared/state/ Shared Flow-based MVI stores
shared/domain-testing Shared fakes for domain and state tests
core/ Design system, generated tokens and shared utilities
features/ Android feature UI modules
platform/ Android and iOS capability implementations
build-logic/ Gradle convention plugins, generators and architecture tests
design/ Reference mockups for the core product surfaces (reserved)
docs/ Current architecture, decisions and team conventions
For the dependency graph and layer responsibilities, see
docs/ARCHITECTURE.md. Product and technical tradeoffs
are recorded in docs/DECISIONS.md, and visual conventions
live in DESIGN.md.
- Gradle Kotlin DSL with a version catalog and convention plugins
- Kotlin Multiplatform, Coroutines, Flow and kotlinx-datetime
- SQLDelight for local persistence
- Koin for dependency injection
- SKIE for Swift-friendly shared APIs
- Jetpack Compose and SwiftUI for native UI
- WorkManager and
UNUserNotificationCenterfor reminders - Platform-native photo capture and storage behind shared ports
- ktlint, detekt, Spotless and Konsist for quality enforcement
Useful repository checks:
./gradlew check
./gradlew :build-logic:test --rerun-tasks
scripts/test-ios.shThe architecture tests intentionally live in build-logic; use
--rerun-tasks when validating dependency-rule changes so Gradle does not reuse a
stale result.
Use short-lived branches and Conventional Commits. Keep changes focused, include
tests for behavior changes, and run the relevant Android and iOS checks before
requesting review. See docs/TEAM_GUIDELINES.md for the
working agreements.
The optional pre-commit hook formats staged Kotlin, Kotlin DSL, and Markdown:
scripts/install-hooks.shSource code is licensed under the Apache License, Version 2.0 — see
LICENSE.
The FluxIt name and brand assets, and the reference mockups in design/, are
reserved and not covered by that license — see LICENSE-ASSETS.
Third-party assets keep their own licenses: the Material Symbols icons in
core/core-designsystem/icons/ are Apache-2.0 (Copyright 2014 Google LLC, see
ATTRIBUTION.md), and the Inter
font files are under the SIL Open Font License 1.1.