A beautiful, modern Pokemon application built with Compose Multiplatform featuring MVI architecture, type-safe navigation, and dynamic theming. Explore Pokemon, manage favorites, and enjoy a seamless experience across Android, Desktop, and iOS platforms.
🎯 Core Features
- 📱 Multiplatform: Android, Desktop, and iOS support
- 🏗️ MVI Architecture: Clean, predictable state management
- 🧭 Type-Safe Navigation: Kotlin Serialization-based routing
- 🎨 Material 3 Design: Modern UI with dynamic theming
- 🌓 Theme Management: Dark mode + Android Dynamic Colors
- 💾 Offline Support: Room database for favorites
- 🔄 Reactive UI: Real-time updates with StateFlow
🐾 Pokemon Features
- 🔍 Pokemon List: Browse all Pokemon with infinite scrolling
- ❤️ Favorites Management: Add/remove Pokemon from favorites
- 📊 Detailed View: Stats, abilities, types, and more
- 🎨 Type-based Theming: Colors based on Pokemon types
- ✨ Shimmer Loading: Beautiful loading animations
🎭 UI/UX Features
- 🌊 Smooth Animations: Page transitions and micro-interactions
- 📱 Adaptive UI: Responsive design for all screen sizes
- 👆 Swipe Actions: Swipe-to-delete favorites
- 🌈 Dynamic Colors: Android 12+ Material You support
- ⚡ Performance: Optimized with lazy loading and caching
- Clone this repository:
git clone https://github.com/Coding-Meet/CMP-MVI-Template.git 
- Open in the latest version of Android Studio or intellij idea.
- This project includes a demo of how to use BuildConfig.
(Note: The API key here is just a placeholder and not used in the app.)
Create or update your local.properties with:
API_KEY=API_KEY Is_Debug_Build=true
- For Android, run the composeAppmodule by selecting theappconfiguration. If you need help with the configuration, follow this link for android
- For iOS, run the composeAppmodule by selecting theiosAppconfiguration. If you need help with the configuration, follow this link for Ios
- For Desktop, run ./gradlew :composeApp:run
- For Desktop with hot reload, run
./gradlew desktopRun -DmainClass=com.example.cmp_mvi_template.MainKt
|  |  |  |  | 
|  |  |  |  | 
|  |  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
📱 Presentation Layer (UI)
├── 🎭 Compose Screens
├── 🧠 ViewModels (MVI)
└── 📊 State Management
💼 Domain Layer (Business Logic)  
├── 📋 Use Cases
├── 🏪 Repository Interfaces
└── 📦 Domain Models
💾 Data Layer (Data Sources)
├── 🌐 Remote (Ktor + PokéAPI)
├── 💿 Local (Room Database)  
└── 🔄 Repository Implementation
- 🎯 UI: Compose Multiplatform + Material 3
- 🏗️ Architecture: MVI + Clean Architecture + Use Cases
- 🧭 Navigation: Compose Navigation + Type-safe routes
- 🌐 Networking: Ktor Client + JSON Serialization
- 💾 Database: Room + SQLite (multiplatform)
- 🎨 Theming: DataStore Preferences + Dynamic Colors
- 🔧 Dependency Injection: Koin
- 🖼️ Images: Coil3 (async image loading)
CMP-MVI-Template/
├── composeApp/                             # ✅ Main Compose Multiplatform app module
│   ├── build.gradle.kts                    # ➕ Gradle config for this module
│   ├── setting.preferences_pb              # 📦 Proto DataStore schema for user settings (theme, etc.)
│
│   └── src/
│       ├── androidMain/                    # 🤖 Android-specific code
│       │   ├── AndroidManifest.xml         # 📄 Manifest file for Android
│       │   └── kotlin/
│       │       └── com/example/cmp_mvi_template/
│       │           ├── MainActivity.kt     # 🚀 Entry point for Android app
│       │           ├── MyApplication.kt    # 🏁 Application class for Koin setup
│       │           └── core/platform/      # 🔌 Android actual implementations for platform interfaces
│
│       ├── iosMain/                        # 🍎 iOS-specific code (uses Kotlin/Native)
│       │   └── kotlin/
│       │       └── com/example/cmp_mvi_template/
│       │           ├── MainViewController.kt # 🧭 iOS screen entry point (UIKit)
│       │           └── core/platform/        # 🔌 iOS actual implementations for platform interfaces
│
│       ├── desktopMain/                    # 🖥 Desktop-specific entry point
│       │   └── kotlin/
│       │       └── com/example/cmp_mvi_template/
│       │           └── main.kt             # 💻 Desktop launcher with ComposeWindow
│
│       ├── commonMain/                     # 🔁 Shared code between all platforms
│       │   ├── composeResources/           # 🎨 Compose Multiplatform resources (fonts, strings, etc.)
│       │   └── kotlin/
│       │       └── com/example/cmp_mvi_template/
│       │
│       │           ├── di/                 # 🧩 Dependency Injection modules using Koin
│       │           │   ├── AppModule.kt
│       │           │   └── PlatformModule.kt
│       │
│       │           ├── app/                # 🌐 App-level shared state (theme, scaffold, etc.)
│       │           │   ├── AppViewModel.kt
│       │           │   └── AppScaffold.kt
│       │
│       │           ├── core/               # 📚 Core layer for base domain, utils, and data sources
│       │           │   ├── utility/        # 🔧 Utility helpers (formatters, validators, etc.)
│       │           │   ├── domain/         # 📦 Base models like pagination, error handling
│       │           │   ├── data/           # 🗃️ Base local + remote source contracts or shared logic
│       │           │   └── platform/       # 🌍 Expect interfaces for platform-specific functionality
│       │
│       │           ├── ui/                 # 🧱 Reusable UI components
│       │           │   ├── Button/
│       │           │   ├── Dialog/
│       │           │   ├── Layout/
│       │           │   └── Theme/          # 🎨 Theme definitions (Typography, Colors, Dimens)
│       │
│       │           └── feature/            # 🌟 Feature modules – each screen or flow has its own folder
│       │               ├── pokemon/        # 🐱 Pokemon feature (MVI pattern)
│       │               │   ├── domain/     # 🔁 Business logic interfaces & models
│       │               │   ├── data/       # 💾 Repository, fake/local/remote data
│       │               │   ├── presentation/ # 🎭 UI state, event, screen, and ViewModel
│       │               │   └── di/         # 🧩 Feature-specific DI
│       │
│       │               ├── setting/        # ⚙️ App settings (e.g., theme selection)
│       │               │   └── presentation/ # 🎭 UI state + screen for settings
│       │
│       │               └── sample_example/ # 🧪 Optional example/template feature
│       │                   ├── presentation/
│       │                   └── domain/
│       ├── commonTest/                     # 🧪 Shared unit tests
│       │   └── com/example/cmp_mvi_template/
│       │       └── ComposeAppCommonTest.kt # ✅ Sample shared test
└── CMP-MVI-Template/
├── composeApp/
│   ├── setting.preferences_pb
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   ├── res/
│       │   │   └── values/
│       │   │       └── strings.xml
│       │   │  
│       │   └── kotlin/
│       │       └── com/
│       │           └── example/
│       │               └── cmp_mvi_template/
│       │                   ├── MyApplication.kt
│       │                   ├── MainActivity.kt
│       │                   └── core/
│       │                       └── platform/
│       │                           ├── theme/
│       │                           │   └── PlatformTheme.android.kt
│       │                           ├── datastore/
│       │                           │   └── createDataStore.android.kt
│       │                           ├── toast/
│       │                           │   ├── AndroidToastManager.kt
│       │                           │   └── ToastManager.android.kt
│       │                           ├── http_engine/
│       │                           │   └── GetHttpClientEngine.android.kt
│       │                           └── database/
│       │                               └── getDatabaseBuilder.android.kt
│       ├── commonMain/
│       │   ├── composeResources/
│       │   │   ├── values/
│       │   │   │   └── strings.xml
│       │   │   └── drawable/
│       │   │       └── compose-multiplatform.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── example/
│       │               └── cmp_mvi_template/
│       │                   ├── di/
│       │                   │   ├── initKoin.kt
│       │                   │   └── CoreModule.kt
│       │                   ├── app/
│       │                   │   ├── presentation/
│       │                   │   │   ├── App.kt
│       │                   │   │   ├── AppThemeState.kt
│       │                   │   │   └── AppViewModel.kt
│       │                   │   └── di/
│       │                   │       └── AppModule.kt
│       │                   ├── core/
│       │                   │   ├── utility/
│       │                   │   │   ├── ComposableExtensions.kt
│       │                   │   │   ├── UiText.kt
│       │                   │   │   ├── IconResource.kt
│       │                   │   │   ├── AppLogger.kt
│       │                   │   │   └── StateController.kt
│       │                   │   ├── domain/
│       │                   │   │   ├── DataErrorToUiTextExtension.kt
│       │                   │   │   ├── Paginator.kt
│       │                   │   │   ├── DataError.kt
│       │                   │   │   ├── Error.kt
│       │                   │   │   └── ResultWrapper.kt
│       │                   │   ├── data/
│       │                   │   │   ├── datastore/
│       │                   │   │   │   ├── ThemeMode.kt
│       │                   │   │   │   └── ThemePreferences.kt
│       │                   │   │   ├── local/
│       │                   │   │   │   ├── PokemonDatabase.kt
│       │                   │   │   │   ├── PokemonDatabaseConstructor.kt
│       │                   │   │   │   └── dao/
│       │                   │   │   │       └── PokemonDao.kt
│       │                   │   │   └── network/
│       │                   │   │       ├── NetworkExtensions.kt
│       │                   │   │       └── HttpClientFactory.kt
│       │                   │   └── platform/
│       │                   │       ├── theme/
│       │                   │       │   └── PlatformTheme.kt
│       │                   │       ├── datastore/
│       │                   │       │   ├── createDataStore.kt
│       │                   │       │   └── AppSettings.kt
│       │                   │       ├── toast/
│       │                   │       │   ├── ToastManager.kt
│       │                   │       │   ├── ToastManagerFactory.kt
│       │                   │       │   └── ToastDuration.kt
│       │                   │       ├── http_engine/
│       │                   │       │   └── GetHttpClientEngine.kt
│       │                   │       └── database/
│       │                   │           └── getDatabaseBuilder.kt
│       │                   ├── ui/
│       │                   │   ├── theme/
│       │                   │   │   ├── AnnotationPreview.kt
│       │                   │   │   ├── Color.kt
│       │                   │   │   ├── Theme.kt
│       │                   │   │   └── Type.kt
│       │                   │   ├── dialog/
│       │                   │   │   ├── ToastPopup.kt
│       │                   │   │   └── LoadingDialog.kt
│       │                   │   ├── navigation/
│       │                   │   │   ├── AppDestination.kt
│       │                   │   │   ├── AdaptiveNavigation.kt
│       │                   │   │   ├── NavigationExtensions.kt
│       │                   │   │   └── bottom_navigation_bar/
│       │                   │   │       ├── NavigationItem.kt
│       │                   │   │       └── BottomNavigationBar.kt
│       │                   │   ├── layout/
│       │                   │   │   └── ErrorMessageLayout.kt
│       │                   │   └── component/
│       │                   │       ├── badges/
│       │                   │       │   └── Badges.kt
│       │                   │       ├── text/
│       │                   │       │   └── Text.kt
│       │                   │       ├── radio_button/
│       │                   │       │   └── RadioButton.kt
│       │                   │       ├── divider/
│       │                   │       │   └── Divider.kt
│       │                   │       ├── navigation_bar/
│       │                   │       │   └── NavigationBar.kt
│       │                   │       ├── progress_indicator/
│       │                   │       │   └── ProgressIndicators.kt
│       │                   │       ├── button/
│       │                   │       │   └── Button.kt
│       │                   │       ├── switch_custom/
│       │                   │       │   └── Switch.kt
│       │                   │       ├── slider/
│       │                   │       │   └── Slider.kt
│       │                   │       ├── icon_button/
│       │                   │       │   └── IconButton.kt
│       │                   │       ├── checkbox/
│       │                   │       │   └── Checkbox.kt
│       │                   │       ├── fab/
│       │                   │       │   └── Fab.kt
│       │                   │       ├── segmented_button/
│       │                   │       │   └── SegmentedButton.kt
│       │                   │       ├── bottom_app_bar/
│       │                   │       │   └── BottomAppBar.kt
│       │                   │       └── top_app_bar/
│       │                   │           └── TopAppBar.kt
│       │                   └── feature/
│       │                       ├── setting/
│       │                       │   ├── di/
│       │                       │   │   └── SettingModule.kt
│       │                       │   └── presentation/
│       │                       │       ├── screen/
│       │                       │       │   ├── SettingScreen.kt
│       │                       │       │   ├── SettingEvent.kt
│       │                       │       │   ├── SettingState.kt
│       │                       │       │   ├── SettingViewModel.kt
│       │                       │       │   └── SettingEffect.kt
│       │                       │       └── component/
│       │                       │           ├── DynamicThemeToggleAndroidOnly.kt
│       │                       │           ├── ThemeSelectionRow.kt
│       │                       │           ├── ThemeSelectionDialog.kt
│       │                       │           └── ThemePreview.kt
│       │                       ├── pokemon/
│       │                       │   ├── di/
│       │                       │   │   └── PokemonModule.kt
│       │                       │   ├── presentation/
│       │                       │   │   ├── component/
│       │                       │   │   │   ├── PokemonCard.kt
│       │                       │   │   │   └── ShimmerEffect.kt
│       │                       │   │   ├── pokemon_details/
│       │                       │   │   │   ├── screen/
│       │                       │   │   │   │   ├── PokemonDetailsViewModel.kt
│       │                       │   │   │   │   ├── PokemonDetailsEvent.kt
│       │                       │   │   │   │   ├── PokemonDetailsScreen.kt
│       │                       │   │   │   │   ├── PokemonDetailsEffect.kt
│       │                       │   │   │   │   └── PokemonDetailsState.kt
│       │                       │   │   │   └── component/
│       │                       │   │   │       ├── PokemonAbilitiesSection.kt
│       │                       │   │   │       ├── PokemonDetailsContent.kt
│       │                       │   │   │       ├── PokemonStatsSection.kt
│       │                       │   │   │       ├── PokemonBasicInfoSection.kt
│       │                       │   │   │       └── PokemonImageSection.kt
│       │                       │   │   ├── pokemon_list/
│       │                       │   │   │   └── screen/
│       │                       │   │   │       ├── PokemonListEvent.kt
│       │                       │   │   │       ├── PokemonListViewModel.kt
│       │                       │   │   │       ├── PokemonListScreen.kt
│       │                       │   │   │       ├── PokemonListEffect.kt
│       │                       │   │   │       └── PokemonListState.kt
│       │                       │   │   └── favorites/
│       │                       │   │       ├── screen/
│       │                       │   │       │   ├── FavoritesState.kt
│       │                       │   │       │   ├── FavoritesScreen.kt
│       │                       │   │       │   ├── FavoritesEffect.kt
│       │                       │   │       │   ├── FavoritesEvent.kt
│       │                       │   │       │   └── FavoritesViewModel.kt
│       │                       │   │       └── component/
│       │                       │   │           ├── SwipeToDeletePokemonCard.kt
│       │                       │   │           ├── FavoritesListContent.kt
│       │                       │   │           └── EmptyFavoritesScreen.kt
│       │                       │   ├── domain/
│       │                       │   │   ├── usecase/
│       │                       │   │   │   ├── GetFavoritesPokemonUseCase.kt
│       │                       │   │   │   ├── GetPokemonDetailsUseCase.kt
│       │                       │   │   │   ├── CheckIfFavoriteUseCase.kt
│       │                       │   │   │   ├── GetPokemonListUseCase.kt
│       │                       │   │   │   └── ToggleFavoriteUseCase.kt
│       │                       │   │   ├── entity/
│       │                       │   │   │   ├── PokemonEntity.kt
│       │                       │   │   │   └── Pokemon.kt
│       │                       │   │   └── repository/
│       │                       │   │       └── PokemonRepository.kt
│       │                       │   └── data/
│       │                       │       ├── mapper/
│       │                       │       │   └── toDomain.kt
│       │                       │       ├── repository/
│       │                       │       │   └── PokemonRepositoryImpl.kt
│       │                       │       └── remote/
│       │                       │           ├── api/
│       │                       │           │   ├── PokemonApiServiceImpl.kt
│       │                       │           │   └── PokemonApiService.kt
│       │                       │           └── dto/
│       │                       │               └── PokemonListResponseDto.kt
│       │                       └── sample_example/
│       │                           ├── di/
│       │                           │   └── SampleExampleModule.kt
│       │                           └── presentation/
│       │                               └── screen/
│       │                                   ├── SampleEffect.kt
│       │                                   ├── SampleExampleScreen.kt
│       │                                   ├── SampleEvent.kt
│       │                                   ├── SampleState.kt
│       │                                   └── SampleExampleViewModel.kt
│       ├── desktopMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── example/
│       │               ├── .DS_Store
│       │               └── cmp_mvi_template/
│       │                   ├── .DS_Store
│       │                   ├── main.kt
│       │                   └── core/
│       │                       └── platform/
│       │                           ├── theme/
│       │                           │   └── PlatformTheme.desktop.kt
│       │                           ├── datastore/
│       │                           │   └── createDataStore.desktop.kt
│       │                           ├── toast/
│       │                           │   ├── RoundedPanel.kt
│       │                           │   ├── ToastManager.desktop.kt
│       │                           │   └── DesktopToastManager.kt
│       │                           ├── http_engine/
│       │                           │   └── GetHttpClientEngine.desktop.kt
│       │                           └── database/
│       │                               └── getDatabaseBuilder.desktop.kt
│       ├── commonTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── example/
│       │               └── cmp_mvi_template/
│       │                   └── ComposeAppCommonTest.kt
│       └── iosMain/
│           └── kotlin/
│               └── com/
│                   └── example/
│                       └── cmp_mvi_template/
│                           ├── MainViewController.kt
│                           └── core/
│                               └── platform/
│                                   ├── theme/
│                                   │   └── PlatformTheme.ios.kt
│                                   ├── datastore/
│                                   │   └── createDataStore.ios.kt
│                                   ├── toast/
│                                   │   ├── ToastManager.ios.kt
│                                   │   └── IosToastManager.kt
│                                   ├── http_engine/
│                                   │   └── GetHttpClientEngine.ios.kt
│                                   └── database/
│                                       └── getDatabaseBuilder.ios.kt
Here are the upcoming tasks and feature enhancements planned for the project:
- 
Koin Annotations Integration - Use Koin Annotation processing (@Single, @Factory, etc.) to simplify and reduce boilerplate for dependency injection.
 
- 
🔄 GraphQL Support for Pokémon API - Implement a GraphQL version of the Pokémon API using Ktor Client. Will be explored in a separate branch for experimentation.
 
- 
🗃️ SQLDelight Sample Integration - Integrate SQLDelight only as a working code sample in a separate module/branch.
- Purpose: Keep reusable code ready for future use or cross-platform Kotlin projects.
- ✅ Room will continue as the primary local storage solution for this app.
 
- 
⚙️ Dev Tooling Scripts (Automation) - Build Gradle or Kotlin-based scripts for:
- 🔁 Renaming package names along with folder structure
- 📦 Creating distributable builds per platform
- 🚀 Auto-generating feature modules with basic files (UI, ViewModel, State, Events) by providing just a feature name
 
 
- Build Gradle or Kotlin-based scripts for:
- 
🧩 Component Showcase Screen - All UI components are implemented but not visible inside the app.
- A new Component Explorer Screen will be added where:
- You can view all components (buttons, cards, inputs, etc.)
- Helpful for testing and design consistency on a real device
 
 
- 
🧪 Unit & Instrumented Testing - Add unit tests for core business logic and ViewModels
- Add instrumented UI tests for critical user flows
- Integrate test coverage tools for quality tracking
 
- 
📱 Maestro Integration (UI Flow Testing) - Set up Maestro to define UI flow test scripts
 
Feel free to contribute to this project by submitting issues, pull requests, or providing valuable feedback. Your contributions are always welcome! 🙌
Give a ⭐️ if this project helped you!
 
Your generosity is greatly appreciated! Thank you for supporting this project.
Meet
