ComposeTemplate is a Jetpack Compose template application that follows Clean Architecture best practices. It simplifies the process of setting up a well-structured Compose application by providing a template with a predefined folder structure. ✨
Report Bug
Request Feature
ComposeTemplate is a Jetpack Compose template application that follows Clean Architecture best practices. It simplifies the process of setting up a well-structured Compose application by providing a template with a predefined folder structure. ✨
- Kotlin - Modern programming language for Android
- MVVM - Architecture pattern
- Jetpack Compose - Modern UI toolkit
- Material 3 - Material Design 3 components
- Navigation3 - Type-safe navigation library
- Retrofit - HTTP client for Android
- Gson - JSON serialization/deserialization
- Hilt - Dependency injection framework
- Kotlin Coroutines - Asynchronous programming
- Kotlin Serialization - Type-safe serialization
- jUnit - Unit testing framework
- MockK - Mocking library for Kotlin
- Truth - Fluent assertions for Java and Android
The project follows Clean Architecture principles with clear separation of concerns and uses Convention Plugins for build configuration:
ComposeTemplate/
├── app/src/main/java/com/ytapps/composetemplate/
│ ├── core/ # Core functionality and utilities
│ │ ├── api/ # API related classes
│ │ │ ├── DefaultInterceptor.kt # HTTP interceptor for auth headers
│ │ │ └── Result.kt # Sealed class for API results
│ │ ├── base/ # Base classes
│ │ │ ├── BaseRepository.kt # Base repository with safeCall
│ │ │ ├── BaseUiState.kt # Base UI state class
│ │ │ └── IMapper.kt # Mapper interface
│ │ ├── di/ # Dependency injection modules
│ │ │ ├── BinderModule.kt # Hilt binding module
│ │ │ └── ProviderModule.kt # Hilt provider module
│ │ ├── navigation/ # Navigation components
│ │ │ ├── NavigationManager.kt # Custom navigation manager
│ │ │ ├── INavigationItem.kt # Navigation item interface
│ │ │ └── IBottomBarItem.kt # Bottom bar item interface
│ │ └── theme/ # UI theme and components
│ │ ├── component/ # Reusable UI components
│ │ │ ├── AppNavigation.kt # Main navigation component
│ │ │ └── AppNavigationBar.kt # Bottom navigation bar
│ │ ├── Color.kt # Color definitions
│ │ ├── Theme.kt # Material 3 theme
│ │ └── Type.kt # Typography definitions
│ ├── data/ # Data layer
│ │ ├── local/ # Local data sources
│ │ │ ├── PreferencesManager.kt # SharedPreferences manager
│ │ │ └── IPreferencesManager.kt # Preferences interface
│ │ ├── model/ # Data models
│ │ │ ├── AuthRequestModel.kt # Auth request DTO
│ │ │ └── AuthResponseModel.kt # Auth response DTO
│ │ ├── remote/ # Remote data sources
│ │ │ └── AuthService.kt # Retrofit API service
│ │ └── repository/ # Repository implementations
│ │ └── AuthRepository.kt # Authentication repository
│ ├── domain/ # Domain layer (business logic)
│ │ ├── mapper/ # Data mappers
│ │ │ └── AuthMapper.kt # Auth model mapper
│ │ ├── model/ # Domain models
│ │ │ └── AuthModel.kt # Auth domain model
│ │ ├── repository/ # Repository interfaces
│ │ │ └── IAuthRepository.kt # Auth repository interface
│ │ └── usecase/ # Use cases
│ │ └── LoginUseCase.kt # Login use case
│ ├── presentation/ # Presentation layer (UI)
│ │ ├── detail/ # Detail screen
│ │ │ ├── DetailRoute.kt
│ │ │ ├── DetailUiState.kt
│ │ │ └── DetailViewModel.kt
│ │ ├── home/ # Home screen
│ │ │ ├── HomeRoute.kt
│ │ │ └── HomeViewModel.kt
│ │ ├── list/ # List screen
│ │ │ ├── ListRoute.kt
│ │ │ ├── ListUiState.kt
│ │ │ └── ListViewModel.kt
│ │ ├── login/ # Login screen
│ │ │ ├── LoginRoute.kt
│ │ │ ├── LoginUiState.kt
│ │ │ └── LoginViewModel.kt
│ │ ├── profile/ # Profile screen
│ │ │ ├── ProfileRoute.kt
│ │ │ └── ProfileViewModel.kt
│ │ ├── search/ # Search screen
│ │ │ ├── SearchRoute.kt
│ │ │ └── SearchViewModel.kt
│ │ └── splash/ # Splash screen
│ │ ├── SplashRoute.kt
│ │ ├── SplashUiState.kt
│ │ └── SplashViewModel.kt
│ ├── util/ # Utilities
│ │ └── Constants.kt # App constants
│ ├── App.kt # Application class with Hilt
│ └── MainActivity.kt # Main activity
├── build-logic/ # Build configuration
│ ├── convention/ # Convention plugins
│ │ └── src/main/kotlin/com/ytapps/composetemplate/convention/
│ │ ├── AndroidApplicationConventionPlugin.kt
│ │ ├── AndroidComposeConventionPlugin.kt
│ │ ├── AndroidHiltConventionPlugin.kt
│ │ ├── AndroidLibraryConventionPlugin.kt
│ │ ├── KotlinAndroid.kt
│ │ └── ProjectExtensions.kt
│ └── README.md # Build logic documentation
└── gradle/
└── libs.versions.toml # Version catalog (dependencies & versions)
This project uses modern Gradle build configuration with Convention Plugins and Version Catalog for maintainable and scalable build logic.
Located in build-logic/convention/, these plugins encapsulate common build configuration:
composetemplate.android.application: Base Android app configuration (SDK versions, Kotlin setup)composetemplate.android.application.compose: Jetpack Compose setup with common dependenciescomposetemplate.android.hilt: Hilt dependency injection configurationcomposetemplate.android.library: Android library module configuration
Benefits:
- ✅ Centralized build configuration
- ✅ Reduced duplication across modules
- ✅ Type-safe Kotlin DSL
- ✅ Easy to maintain and update
All dependencies and versions are managed in gradle/libs.versions.toml:
[versions]
minSdk = "23"
compileSdk = "36"
targetSdk = "36"
versionCode = "1"
versionName = "1.0.0"
kotlin = "2.2.10"
[libraries]
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }Benefits:
- ✅ Single source of truth for versions
- ✅ Type-safe dependency accessors
- ✅ Easy version updates
- ✅ Shared across all modules
For detailed build configuration documentation, see build-logic/README.md.
- Navigation3 Integration: Uses the latest Navigation3 library with type-safe navigation
- Custom NavigationManager: Flexible navigation management with back stack handling
- Bottom Navigation Bar: Material 3 adaptive bottom navigation bar
- Route-based Navigation: Serializable route objects for type-safe navigation
- Navigation Methods:
navigate()- Navigate to a new screennavigateBack()- Navigate back in the stacknavigateOver()- Navigate over a specific routenavigateToTop()- Navigate to top of stackselectTab()- Select bottom bar tab
- Splash Screen: Initial screen with routing logic
- Login Screen: Authentication screen with login flow
- Home Screen: Main home screen with navigation examples
- Search Screen: Search functionality screen
- Profile Screen: User profile screen
- List Screen: List view example
- Detail Screen: Detail view example
- Retrofit Integration: Configured with Gson converter
- DefaultInterceptor: Automatic token injection and refresh token handling
- Adds Authorization header to all requests
- Handles 401 Unauthorized responses
- Automatic token refresh on authentication failure
- BaseRepository: Safe API call wrapper with error handling
safeCall()function for error-safe network calls- Returns
Result<T>sealed class (Success/Error)
- PreferencesManager: Local data storage using SharedPreferences
- Token management (access token, token type)
- User credentials storage
- User session management
- Repository Pattern: Clean separation between data sources
- Mapper Pattern: Data model transformation between layers
- Hilt Integration: Full dependency injection setup
- ProviderModule: Provides network components (Retrofit, OkHttp, Gson)
- BinderModule: Binds interfaces to implementations
- Singleton Components: Properly scoped dependencies
- Unit Tests: Comprehensive test coverage with JUnit
- MockK: Mocking framework for Kotlin
- Truth: Fluent assertions library
- Test Examples:
- Repository tests
- ViewModel tests
- Mapper tests
- Use case tests
- Base repository tests
- Material 3 Design: Latest Material Design components
- Custom Theme: Themed colors and typography
- Adaptive Navigation: Material 3 adaptive navigation components
- Compose UI: Fully built with Jetpack Compose
- Header and Refresh Token Interceptor: Automatic token management with refresh token support DefaultInterceptor.kt
- Safe Network Calls: Error-handled network function BaseRepository.kt
- Flexible Navigation Structure: Custom navigation manager with Navigation3 NavigationManager.kt
- Preferences Manager: Local data storage with SharedPreferences PreferencesManager.kt
- Auth Flow: Complete authentication flow with token management AuthRepository.kt
- Clean Architecture: Complete separation of Data, Domain, and Presentation layers
- Unit Tests: All structures tested with JUnit and MockK Examples
- End-to-End Examples: Complete examples for all architecture layers
- Android Studio Hedgehog (2023.1.1) or later
- JDK 17 or later
- Android SDK with API level 23 (Android 6.0) or higher
- Gradle 8.13.0 or later
- Clone the repository
git clone https://github.com/mustafayigitt/ComposeTemplate.git
cd ComposeTemplate- Configure Base URLs
Create or update gradle.properties in the project root with your API base URLs:
BASE_URL_DEBUG=https://your-debug-api-url.com/
BASE_URL=https://your-production-api-url.com/- Run the Initializer Script
To create a new project using this template:
chmod +x initializer.sh
./initializer.shThe script provides an interactive setup with:
- Input Validation: Ensures valid application ID and name formats
- Configuration Summary: Shows all settings before proceeding
- Confirmation Prompt: Asks for confirmation before creating the project
When prompted:
- Enter your
applicationId(e.g.,com.example.myapp)- Must be lowercase with at least 2 segments (e.g.,
com.example) - Only letters, numbers, and underscores allowed
- Must be lowercase with at least 2 segments (e.g.,
- Enter your
applicationName(e.g.,MyApp)- Must start with a letter
- Only alphanumeric characters allowed
The script will:
- ✅ Validate your inputs
- ✅ Create a new project directory with your application name
- ✅ Restructure all source directories (app + build-logic)
- ✅ Replace all package names and references
- ✅ Update convention plugin package names and IDs
- ✅ Generate a proper
.gitignorefile - ✅ Initialize a new git repository with initial commit
- ✅ Clean up template-specific files
- ✅ Provide clear next steps
- Open the Project
Open the newly created project in Android Studio:
- If using the template directly: Open
ComposeTemplatefolder - If using initializer: Open the created project folder (e.g.,
../MyApp)
- Sync and Build
- Sync Gradle files
- Build the project (Build > Make Project)
- Run on an emulator or device
The project uses build variants for different environments:
- Debug: Uses
BASE_URL_DEBUGfromgradle.properties - Release: Uses
BASE_URLfromgradle.properties
- minSdk: 23 (Android 6.0)
- targetSdk: 36
- compileSdk: 36
- Java Version: 17
- Kotlin Version: 2.2.10
All dependencies are managed through gradle/libs.versions.toml using Version Catalogs. Key dependencies include:
- Compose BOM: 2024.06.00
- Hilt: 2.57.1
- Retrofit: 2.11.0
- Navigation3: 1.0.0
- Kotlin: 2.2.10
- Create Route Object (in
presentation/yourfeature/YourFeatureRoute.kt):
@Serializable
data object YourFeature : INavigationItem {
override val route: String = "route_your_feature"
@Composable
override fun ContentScreen(navigationManager: NavigationManager) {
YourFeatureScreen(navigationManager = navigationManager)
}
}- Create Screen Composable:
@Composable
fun YourFeatureScreen(
navigationManager: NavigationManager,
viewModel: YourFeatureViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
// Your UI implementation
}- Add to NavigationManager (if needed for bottom bar):
val bottomBarItems: List<IBottomBarItem> = listOf(
Home,
Search,
YourFeature, // Add here
Profile,
)- Create API Service (in
data/remote/YourService.kt):
interface YourService {
@POST("your-endpoint")
suspend fun yourMethod(@Body request: YourRequestModel): Response<YourResponseModel>
}- Create Repository (in
data/repository/YourRepository.kt):
class YourRepository @Inject constructor(
private val yourService: YourService
) : BaseRepository(), IYourRepository {
override suspend fun yourMethod(): Result<YourResponseModel> {
return safeCall {
yourService.yourMethod(YourRequestModel())
}
}
}- Use in ViewModel:
@HiltViewModel
class YourViewModel @Inject constructor(
private val repository: IYourRepository
) : ViewModel() {
fun loadData() {
viewModelScope.launch {
when (val result = repository.yourMethod()) {
is Result.Success -> {
// Handle success
}
is Result.Error -> {
// Handle error
}
}
}
}
}// Navigate to a new screen
navigationManager.navigate(YourFeature)
// Navigate back
navigationManager.navigateBack()
// Navigate over a specific route
navigationManager.navigateOver(NewRoute, OldRoute)
// Navigate to top of stack
navigationManager.navigateToTop(Home)// In your repository or use case
@Inject constructor(
private val prefs: IPreferencesManager
) {
// Save data
prefs.saveString("key", "value")
// Get data
val value = prefs.getString("key", "default")
// Save credentials (for auth)
prefs.saveCredentials(authResponse)
}This project follows Clean Architecture principles with three main layers:
- Responsibility: Data sources (remote API, local storage)
- Components: Repositories, Data Models, API Services, Local Storage
- Location:
data/package
- Responsibility: Business logic and use cases
- Components: Use Cases, Domain Models, Repository Interfaces, Mappers
- Location:
domain/package
- Responsibility: UI and user interactions
- Components: ViewModels, UI States, Composable Screens, Routes
- Location:
presentation/package
- Responsibility: Shared utilities and base classes
- Components: Base classes, Navigation, DI modules, Theme, API utilities
- Location:
core/package
The project includes comprehensive unit tests. Run tests with:
./gradlew test- Repository Tests: Test data layer logic
- ViewModel Tests: Test presentation layer logic
- Mapper Tests: Test data transformation
- Use Case Tests: Test business logic
Example test:
@Test
fun `test login success`() = runTest {
// Given
val expectedResult = Result.Success(authResponse)
coEvery { authService.login(any()) } returns Response.success(authResponse)
// When
val result = authRepository.login(authRequest)
// Then
assertThat(result).isInstanceOf(Result.Success::class.java)
}Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- If you have suggestions for adding or removing projects, feel free to open an issue to discuss it, or directly create a pull request after you edit the README.md file with necessary changes.
- Please make sure you check your spelling and grammar.
- Create individual PR for each suggestion.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
