Modern Kotlin Android app template with Jetpack Compose, Hilt, Navigation 3, Ktor, Room/Paging, Paparazzi, and a modular MVVM/Clean Architecture setup.
This repository intentionally demonstrates a production-ready architecture suited for teams that need to scale quickly and collaborate across multiple features. Elements such as multi-module structure, repository and use-case layers, and other advanced patterns are included to showcase how large, collaborative projects can stay maintainable over time. They may be unnecessary for smaller apps or teams with different preferences. Evaluate each practice against your team's size, project scope, and delivery goals before adopting it wholesale.
This project serves as a starter kit for new Android applications. It's designed to save you from the repetitive setup process of configuring dependencies, setting up architecture, and implementing common utilities. Use the template, rename the package and feature placeholders, then start building your app's features.
This template is packed with modern libraries and tools from the Android ecosystem:
- Tech Stack: 100% Kotlin
- UI: Jetpack Compose for declarative UI.
- Theming: Material 3 (Material You) support.
- Navigation: Compose Navigation 3 for all screen transitions.
- Architecture: Follows Google's official "Guide to app architecture".
- MVVM (Model-View-ViewModel).
- UI Layer: State-driven UI with
ViewModel,State, andActions. - Domain Layer: (Optional but recommended) for business logic.
- Data Layer:
Repositorypattern.
- Asynchronous work: Kotlin Coroutines & Flows for managing background threads and streams of data.
- Dependency Injection: Hilt for managing dependencies.
- Networking: Ktor Client for efficient REST API communication.
- Serialization: Kotlinx.serialization (used with Ktor) for fast and modern JSON parsing.
- Build Configuration: Kotlin explicit API mode is enabled across modules to keep public APIs intentional and well-documented.
- Testing:
- Unit Tests: JUnit 4
- Screenshot Tests: Paparazzi
- UI Tests: Compose Test Rules testing both screen orientations and multiple font scales.
- Integration Tests: Instrumented Compose flows cover the full stack—from
navigation orchestration to feature screens using Hilt-enabled end-to-end journeys and
parameterized scenarios that sweep multiple font scales and orientations
via the shared
BaseUiTestharness. - Test Doubles Philosophy: The template deliberately avoids runtime mocking frameworks. Instead, it ships purpose-built fakes that behave like lightweight in-memory implementations of production contracts. This approach keeps tests fast and deterministic, encourages explicit collaboration boundaries, and allows your team to reuse the same fakes across unit, integration, and end-to-end scenarios without the brittleness often introduced by mocks.
Execute the following Gradle tasks from the project root to run the automated test suites locally:
ℹ️ Run Gradle with JDK 21 or newer.
| Suite | Command | Description |
|---|---|---|
| Local/unit tests | ./gradlew testDebug |
Executes JVM-based tests located under src/test for the debug build variant. |
| Instrumented Android tests | ./gradlew connectedDebugAndroidTest |
Launches Espresso/Compose instrumentation tests in src/androidTest on a connected emulator or device. |
ℹ️ Ensure that an Android emulator or physical device is connected before running the instrumentation suite.
- Dynamic system theming: The
presentationlayer streams system theme and contrast updates, maps them through thedata/system-featuresmodule, and applies them across the Compose UI while synchronizing system bars for a fully adaptive experience. - Offline-first paging: The
data:charactersmodule layers Room DAOs, Paging 3, and aRemoteMediatoron top of the Ktor client so character feeds stay cached and transparently refresh whenever the network is available. - Navigation infrastructure: A dedicated Compose Navigation 3 stack coordinates screen changes
via a
NavigationManager, withNavigatorcomponents observing command channels and handling saveable state, back stack pops, and simultaneous navigation requests. All routing code lives in the dedicatedpresentation:navigationmodule so feature modules stay navigation-agnostic and decoupled from the implementation details. - Snapshot tooling: The
presentationmodule includes a Paparazzi test toolkit featuring a custom Pixel 10 Pro XL device profile and helpers that generate day/night parameter sets for rich screenshot coverage. - Preview ergonomics:
presentation:previewspackages reusable Compose@Previewannotations so feature teams can opt into consistent device sizes, light/dark themes, and dynamic color toggles without re-declaring boilerplate in every screen file. - Coroutine utilities: The
utilsmodule ships reusable coroutine extensions such asstateInWhileSubscribedandobserveConfigurationChangesfor concise and lifecycle-aware state management in ViewModels. - Build hygiene: Gradle is configured for Kotlin explicit API mode, context receivers, and the Versions Plugin to keep dependencies up to date and compiler flags consistent across modules.
- Testable system features: The
data/system-featuresmodule exposes fake managers backed by Turbine-based tests, showcasing how to stub system services when exercising the settings feature. - Streamlined startup: A Hilt-enabled
Application, splash activity, and edge-to-edgeAppShellActivitycombine with the navigation manager to launch directly into themed Compose content with minimal boilerplate. - Edge-to-edge ready: Compose screens are rendered behind the system bars, with window insets managed centrally so features automatically inherit full-height layouts.
- Multipane large-screen layouts: Adaptive list-detail experiences that keep the character catalog and the selected detail visible side by side on tablets, foldables, and landscape desktops.
This template uses a state-driven MVVM (Model-View-ViewModel) architecture combined with principles from Clean Architecture.
- UI (Compose): Observes
Statefrom theViewModeland sendsActions(user actions) to it. It is passive and dumb. - ViewModel: Follows
a declarative state holder approach.
Handles business logic for the screen. It consumes
Actions, interacts with UseCases/Repositories, and exposes a singleStateFlow for the UI to observe. - UseCases (Domain Layer): (Optional) Encapsulates a single piece of business logic (e.g.,
GetCharacter). This makes logic reusable and easier to test. - Repository (Data Layer): The single source of truth for data. It abstracts away the data
source (network or local database) and provides a clean API for the
ViewModelorUseCasesto consume.
The repository is laid out as a layered, multi-module Gradle project. Each directory below maps to a distinct slice of the architecture so teams can develop features independently while keeping dependencies explicit.
├── app/ # Android entry point and app-level configuration
├── data/ # Repository implementations, remote clients, and system services
├── domain/ # Business rules, models, and use cases shared by features
├── di/ # Centralized Hilt bindings that wire modules together
├── presentation/ # Compose UI, navigation stack, previews, and design system
├── utils/ # Cross-cutting helpers (coroutines, lifecycle, etc.)
├── buildSrc/ # Gradle convention plugins and dependency catalogs
└── gradle/, *.gradle.kts # Build logic, settings, and version configuration
Every Gradle module has a single responsibility. Use the table below to find the code you need:
- Application shell
app: Hosts theApplication, activities, and wires the dependency graph at runtime.
- Presentation layer
presentation:design-system: Shared Compose theming, typography, and reusable components.presentation:app-shell: Owns the app shell and top-level screen composition.presentation:features:: Feature-specific screens such ascharacters-listandcharacter-details.presentation:navigation&presentation:navigation-impl: Navigation contracts and their Compose Navigation 3 implementation.presentation:paparazzi,presentation:previews,presentation:utils,presentation:fixtures: Tooling for previews, snapshot tests, and sample data.
- Domain layer
domain:core-models: Canonical models exchanged between layers.domain:characters,domain:settings,domain:system-features: Use cases and business logic per feature area.
- Data layer
data:characters: Paging, Room cache, and remote mediator for list and detail flows.data:network: Ktor client configuration and API definitions.data:settings: Persistence for user preferences and configuration toggles.data:system-features: Abstractions over device capabilities with test fakes.
- Dependency injection
di: Shared Hilt modules and component wiring consumed across the app.
- Shared utilities
utils:lifecycle: Lifecycle-aware coroutine helpers and Flow extensions reused in ViewModels.
-
Create Your Repository: Click the "Use this template" button on the GitHub repository page. This will create a new repository in your account with the same file structure.
-
Clone Your New Repository:
git clone https://github.com/<your-github-user>/<your-repo>.git cd <your-repo>
-
Rename Package Name: The current package name is
com.sottti.android.app.template. To rename it:- In Android Studio, right-click the root
com.sottti.android.app.templatepackage in thekotlindirectory. - Select Refactor > Rename.
- In the dialog, choose Rename package.
- Enter your new package name (e.g.,
com.mycompany.myapp). - Click Do Refactor.
- Manually update the
applicationIdinapp/build.gradle.ktsto match your new package name.
- In Android Studio, right-click the root
-
Sync & Build: Sync the Gradle files and build the project to ensure everything is set up correctly.
-
Start Coding! You're all set. Start building your app's unique features.
This is a template for your projects, but if you have ideas on how to improve the template itself, contributions are welcome!
See CONTRIBUTING.md for the contribution workflow and pull request expectations.
This project is licensed under the MIT License - see the LICENSE file for details.
If you feel like saying hi, have any comments, suggestions or questions, open an issue or say hi 👋 on X.









