A production-grade Android application built with Kotlin, Jetpack Compose, Material Design 3, and Clean Architecture. This project serves as a scalable template for modern Android development.
- Tech Stack
- Architecture
- Project Structure
- Prerequisites
- Getting Started
- Features
- Screenshots
- Testing
- Build & Release
- Contributing
- License
| Technology | Version | Purpose |
|---|---|---|
| Kotlin | 2.0.21 | Primary language |
| Jetpack Compose | BOM 2025.06 | Declarative UI |
| Material Design 3 | Latest | UI components & theming |
| Hilt | 2.52 | Dependency injection |
| Room | 2.6.1 | Local SQLite database |
| Retrofit | 2.11.0 | REST API client |
| OkHttp | 4.12.0 | HTTP client + logging |
| Coroutines | 1.9.0 | Asynchronous programming |
| Compose Navigation | 2.8.5 | In-app navigation |
| KSP | 2.0.21-1.0.28 | Annotation processing |
| AGP | 8.7.0 | Android Gradle Plugin |
| compileSdk | 35 | Target Android API |
| minSdk | 26 | Android 8.0+ support |
This project follows Clean Architecture with MVVM pattern:
Presentation Layer (UI + ViewModel)
↓
Domain Layer (Use Cases + Repository Interface + Models)
↓
Data Layer (RepositoryImpl + Room DAO + Retrofit API)
↓
Local DB ←——→ Remote API
- Single Source of Truth: Room database is the authoritative data source
- Reactive UI: All reads return
Flow<T>— Compose auto-recomposes on data changes - Separation of Concerns: UI, business logic, and data access are strictly separated
- Dependency Inversion: Domain layer depends on abstractions, not implementations
- Error Handling:
Result<T>sealed class encapsulates Loading/Success/Error states
com.ussu321.template/
│
├── core/ # Scalable foundation layer
│ ├── network/
│ │ └── ApiResult.kt # Generic API result wrapper + safeApiCall()
│ ├── dispatcher/
│ │ └── CoroutineDispatcherModule.kt # Injectable dispatchers (@IoDispatcher, etc.)
│ ├── errors/
│ │ ├── AppException.kt # Typed exception hierarchy
│ │ └── ErrorMapper.kt # Throwable → AppException + UI string mapping
│ └── extensions/
│ ├── ContextExt.kt # toast(), toastLong()
│ ├── FlowExt.kt # Flow<T>.asResult()
│ ├── DateExt.kt # Long.toFormattedDate(), Long.toRelativeDate()
│ └── StringExt.kt # ifBlankOrNull(), truncate()
│
├── common/ # Reusable UI building blocks
│ ├── loading/
│ │ ├── LoadingIndicator.kt # Centered circular progress
│ │ ├── FullScreenLoading.kt # Scrim overlay with spinner
│ │ └── ShimmerLoading.kt # Animated shimmer skeleton
│ ├── dialogs/
│ │ ├── ConfirmationDialog.kt # Reusable confirm/dismiss dialog
│ │ └── InfoDialog.kt # Single-button info dialog
│ ├── widgets/
│ │ ├── Spacers.kt # Consistent spacing composables
│ │ └── AppLogo.kt # Standardized icon wrapper
│ └── animations/
│ ├── FadeIn.kt # Fade animation wrapper
│ ├── SlideIn.kt # Vertical slide animation
│ └── ScaleIn.kt # Scale animation wrapper
│
├── data/ # Data layer
│ ├── local/
│ │ ├── AppDatabase.kt # Room database singleton
│ │ └── TaskDao.kt # Data Access Object for tasks
│ ├── model/
│ │ └── TaskEntity.kt # Room entity
│ ├── remote/
│ │ ├── TaskApiService.kt # Retrofit API interface
│ │ ├── dto/
│ │ │ └── TaskDto.kt # JSON data transfer object
│ │ └── mapper/
│ │ ├── TaskDtoMapper.kt # DTO ↔ Entity, DTO ↔ Domain mappings
│ │ └── TaskEntityMapper.kt # Entity ↔ Domain mappings
│ └── repository/
│ └── TaskRepositoryImpl.kt # Concrete repository implementation
│
├── domain/ # Domain layer (framework-independent)
│ ├── model/
│ │ ├── Result.kt # Sealed class: Loading/Success/Error
│ │ └── Task.kt # Domain model
│ ├── repository/
│ │ └── TaskRepository.kt # Repository interface contract
│ └── usecase/
│ ├── GetAllTasksUseCase.kt
│ ├── GetTaskByIdUseCase.kt
│ ├── AddTaskUseCase.kt
│ ├── UpdateTaskUseCase.kt
│ ├── DeleteTaskUseCase.kt
│ ├── ToggleTaskCompletionUseCase.kt
│ └── GetActiveTaskCountUseCase.kt
│
├── di/
│ └── AppModule.kt # Hilt dependency providers
│
├── ui/ # Presentation layer
│ ├── theme/
│ │ ├── Color.kt # Light/dark color palettes
│ │ ├── Theme.kt # Dynamic color + system bar theming
│ │ └── Type.kt # Material 3 typography scale
│ ├── navigation/
│ │ ├── Screen.kt # Type-safe route definitions
│ │ └── AppNavigation.kt # NavHost with navigation graph
│ ├── components/
│ │ ├── TaskCard.kt # Task list item card
│ │ ├── PriorityChip.kt # Priority level indicator chip
│ │ ├── EmptyState.kt # Empty list illustration
│ │ └── AddTaskDialog.kt # Task creation dialog
│ └── screens/
│ ├── home/
│ │ └── HomeScreen.kt # Main task list screen
│ └── detail/
│ └── TaskDetailScreen.kt # Task detail view
│
├── viewmodel/
│ ├── HomeViewModel.kt # Home screen state management
│ └── TaskDetailViewModel.kt # Detail screen state management
│
├── utils/
│ └── Constants.kt # App-wide constants
│
├── MainActivity.kt # Entry point with edge-to-edge
└── MyApplication.kt # @HiltAndroidApp application class
- Android Studio Koala or Quail (2026.x) or later
- JDK 17 or higher
- Android SDK with API 35 installed
- Git (optional, for version control)
# If using Git
git clone <repository-url>
cd MyApp
# Or extract the ZIP file and open the `android_project/` folder- Launch Android Studio
- Select "Open" and choose the
android_project/directory - Wait for Gradle sync to complete (may take 2–5 minutes on first open)
# Via Android Studio: Build → Make Project (Ctrl+F9)
# Or via terminal:
./gradlew assembleDebug- Physical device: Enable USB debugging, connect via USB, select device in Android Studio
- Emulator: Create an AVD with API 26+ (recommend API 33+ for best experience)
- Click Run (Shift+F10) or use the green play button
| Feature | Description |
|---|---|
| ✅ Task List | View all tasks sorted by priority and creation time |
| ✅ Add Task | Create tasks with title, description, and priority (Low/Medium/High) |
| ✅ Task Detail | View full task information including creation date |
| ✅ Toggle Completion | Mark tasks as completed/incomplete with checkbox |
| ✅ Delete Task | Remove tasks with confirmation dialog |
| ✅ Empty State | Friendly illustration when no tasks exist |
| ✅ Reactive UI | Auto-updates when database changes via Flow |
| ✅ Error Handling | Snackbar notifications for operation failures |
| ✅ Material You | Dynamic color theming on Android 12+ |
| ✅ Dark Mode | Automatic light/dark theme switching |
| Feature | Benefit |
|---|---|
| Extracted Mappers | TaskDtoMapper & TaskEntityMapper are testable and reusable |
| Injectable Dispatchers | Easy test substitution with Dispatchers.Unconfined |
| Typed Exceptions | AppException hierarchy enables precise error handling |
| Safe API Calls | safeApiCall() wrapper prevents crashes from network issues |
| Extension Functions | Clean, reusable utilities (toFormattedDate(), asResult()) |
| Reusable Components | ConfirmationDialog, ShimmerLoading, Spacer16() reduce duplication |
# Run all unit tests
./gradlew test
# Run tests for a specific module
./gradlew :app:testDebugUnitTest# Run instrumented tests on connected device/emulator
./gradlew connectedAndroidTest- Mapper Tests: Verify
TaskDtoMapperandTaskEntityMapperconversions - Repository Tests: Mock
TaskDaoandTaskApiServiceto testTaskRepositoryImpl - ViewModel Tests: Use
kotlinx-coroutines-testwithDispatchers.Unconfined - UI Tests: Use Compose Testing framework for screen navigation and interactions
./gradlew assembleDebug
# Output: app/build/outputs/apk/debug/app-debug.apk# Requires signing configuration in app/build.gradle.kts
./gradlew assembleRelease
# Output: app/build/outputs/apk/release/app-release.apkRelease builds automatically enable:
- Code shrinking (
isMinifyEnabled = true) - Resource shrinking (
isShrinkResources = true) - ProGuard rules in
app/proguard-rules.pro
Edit app/src/main/res/values/strings.xml:
<string name="app_name">Your App Name</string>- Refactor
com.ussu321.templateto your desired package in Android Studio - Update
namespaceandapplicationIdinapp/build.gradle.kts - Update
AndroidManifest.xmlif needed
- Update
BASE_URLinutils/Constants.kt - Modify
TaskApiService.ktendpoints to match your API - Adjust
TaskDtofields andTaskDtoMapperif response format differs
- Add route to
ui/navigation/Screen.kt - Register in
ui/navigation/AppNavigation.kt - Create screen composable in
ui/screens/ - Create ViewModel in
viewmodel/(if needed)
- Create entity in
data/model/ - Add to
AppDatabase.kt@Database(entities = [...]) - Create DAO in
data/local/ - Provide DAO in
di/AppModule.kt
| Issue | Solution |
|---|---|
| Gradle sync fails | Check JDK 17 is selected: File → Settings → Build → Gradle |
| KSP errors | Ensure KSP plugin version matches Kotlin version |
| Room compilation errors | Check @Entity and @Dao annotations are correct |
| Hilt injection fails | Verify @HiltAndroidApp on MyApplication and @AndroidEntryPoint on MainActivity |
| Compose preview not working | Ensure debugImplementation(libs.androidx.ui.tooling) is present |
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit changes:
git commit -m "Add my feature" - Push to branch:
git push origin feature/my-feature - Open a Pull Request
Copyright 2026 ussu321
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
- Android Developers — Official documentation and best practices
- Jetpack Compose — Modern Android UI toolkit
- Material Design 3 — Design system and components
- Dagger Hilt — Dependency injection framework
Built with ❤️ using Kotlin, Jetpack Compose, and Clean Architecture.