Creating features in Android development often involves repetitive tasks like setting up screen files, UI state management, and ViewModel classes. To streamline this process, we can use a Gradle Task to automatically generate these files based on reusable templates. This article will guide you step-by-step on how to implement and use this Gradle-based solution.
What Will Be Generated?
For every new screen, the Gradle task will generate:
<ScreenName>Screen.kt: A composable function that defines the screen’s UI.
<ScreenName>UiState.kt: A data class representing the screen’s UI state and events.
<ScreenName>ViewModel.kt: A ViewModel that manages the UI state and handles user interactions.
How It Works
The custom Gradle task simplifies the process of generating new feature files in Android by using pre-defined templates. These templates are placed in the featureTemplate directory and are used to create new screen features with consistent structure and code.
1. Directory Structure and Template Files
The Gradle task looks for templates in a directory called featureTemplate. Inside this directory, you can organize different feature templates into subfolders. Each subfolder corresponds to a template type. For example, the
screenTemplate subfolder will contain the basic files needed to generate a new screen, such as
Screen.kt,
UiState.kt, and
ViewModel.kt.
Example of the directory structure:
featureTemplate/ ├── screenTemplate/ │ ├── Screen.kt │ ├── UiState.kt │ └── ViewModel.kt
2. Generated Directory Structure
When you run the Gradle task to generate a new feature, the task creates a new directory for the feature in your project’s src/main/java/com/example/app/ folder. This generated directory will contain files like:
src/main/java/com/example/app/<FeatureName>/ ├── <FeatureName>Screen.kt ├── <FeatureName>UiState.kt └── <FeatureName>ViewModel.kt
For instance, if you create a feature called Home, the task will generate:
src/main/java/com/example/app/home/ ├── HomeScreen.kt ├── HomeUiState.kt └── HomeViewModel.kt
3. Template Customization
The templates in the featureTemplate directory use placeholders like
{{FeatureName}},
{{packageName}}, and
{{className}}. When the Gradle task runs, these placeholders are automatically replaced with actual values based on the
featureName you provide and the configuration of your project.
For example, if your feature name is Home, the template file
Screen.kt might look like this:
Template (Screen.kt):
package {{packageName}} import androidx.compose.runtime.Composable @Composable fun {{className}}Screen() { // TODO: Implement the UI for {{className}}Screen }
When you run the task with -PfeatureName=Home, the task will replace
{{packageName}} with your base package name (e.g.,
com.example.app) and
{{className}} with
Home. This results in the following generated file:
Generated (HomeScreen.kt):
package com.example.app import androidx.compose.runtime.Composable @Composable fun HomeScreen() { // TODO: Implement the UI for HomeScreen }
This customization process ensures that each feature file is tailored to your project without manual editing.
Implementation of Feature Template Automation in Android Project
To implement the automation of feature generation in your Android project, follow the steps outlined below. These steps will guide you in setting up the necessary Gradle task, preparing template files, and using those templates to generate new features automatically.
Step 1: Setting Up the Gradle Task
The first step is to define a custom Gradle task in your build.gradle.kts file. This task will automatically generate the necessary feature files based on the template you specify.
How It Works:
- Defining Properties: The task will accept two properties:
featureName: The name of the feature you want to generate (e.g.,
Home).
templateName: The type of template you want to use (e.g.,
screenTemplate).
rootFolderName: Identifies the directory containing your template files (default is
featureTemplate).
2. Code Replacement: The task will read the template files and replace placeholders ({{featureName}},
{{className}}, and
{{packageName}}) with actual values based on your project configuration.
3. Directory Structure: The task creates a new directory structure for the feature under src/main/java/<packageName>/<FeatureName>/ and generates the required files.
Code Implementation:
// build.gradle.kts (Module :app) tasks.register("moveTemplateToFeature") { val rootFolderName = "featureTemplate" val featureName = project.findProperty("featureName") as String? ?: error("Feature name is required. Use -PfeatureName=<name> to specify it.") val templateName = project.findProperty("templateName") as String? ?: error("Template name is required. Use -PtemplateName=<name> to specify it.") // Retrieve the base package name from the project configuration val basePackageName = project.android.defaultConfig.applicationId ?: error("Base package could not be detected. Ensure applicationId is set in defaultConfig.") // Construct the base output path for the generated files val generatedFilePath = File(projectDir, "src/main/java/${basePackageName.replace(".", "/")}") // Map of placeholders to be replaced in template files val placeholders = mapOf( "{{packageName}}" to basePackageName, "{{featureName}}" to featureName.lowercase(), "{{className}}" to featureName.replaceFirstChar { it.titlecase(Locale.getDefault()) } ) doLast { val featureDir = File(generatedFilePath, featureName.lowercase()) if (featureDir.exists()) error("Feature '$featureName' already exists at ${featureDir.path}.") featureDir.mkdirs() val templateDir = File(rootDir, "$rootFolderName/$templateName") check(templateDir.exists() && templateDir.isDirectory) { "Template directory '$templateDir' not found or is not a directory." } // Copy and customize template files templateDir.walkTopDown() .filter { it.isFile } .forEach { file -> val relativePath = file.relativeTo(templateDir).parent ?: "" val featureFileName = "${featureName}${file.name}" val targetFile = File(featureDir, "$relativePath/$featureFileName") targetFile.parentFile.mkdirs() // Read file content and replace placeholders var content = file.readText() placeholders.forEach { (key, value) -> content = content.replace(key, value) } targetFile.writeText(content) println("Generated file: ${targetFile.path}") } println("Feature '$featureName' successfully generated at ${featureDir.path}.") } }
Step 2: Preparing the Template Files
The next step is to create the templates for the feature files. These templates will contain placeholders that will be replaced by the Gradle task during generation.
Template Folder Structure
Create a featureTemplate/ directory and then a subfolder for each template type (e.g.,
screenTemplate). Inside the
screenTemplate/ folder, create the following files:
featureTemplate/ ├── screenTemplate/ │ ├── Screen.kt │ ├── UiState.kt │ └── ViewModel.kt
Screen Template (Screen.kt)
package {{packageName}} import androidx.compose.runtime.Composable @Composable fun {{className}}Screen() { // TODO: Implement the UI for {{className}}Screen }
UI State Template (
UiState.kt)
package {{packageName}} data class {{className}}UiState( val message: String = "" ) sealed class {{className}}UiEvent { object OnButtonClick : {{className}}UiEvent() }
ViewModel Template (ViewModel.kt)
package {{packageName}} import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow class {{className}}ViewModel : ViewModel() { private val _uiState = MutableStateFlow({{className}}UiState()) val uiState: StateFlow<{{className}}UiState> = _uiState fun handleEvent(event: {{className}}UiEvent) { when (event) { is {{className}}UiEvent.OnButtonClick -> { _uiState.value = {{className}}UiState("Button Clicked!") } } } }
Job Offers
Step 3: Generating a Feature
Once the Gradle task and templates are set up, you can now generate new features by running a Gradle command.
How to Run:
Run the following command from the terminal:
./gradlew moveTemplateToFeature -PfeatureName=Home -PtemplateName=screenTemplate
featureName should be replaced with the name of the feature you want to create (e.g.,
Home).
templateName refers to the folder where your templates are stored (e.g.,
screenTemplate).
Generated Files
After running the command, the following files will be generated in the appropriate location:
src/main/java/com/example/app/home/ ├── HomeScreen.kt ├── HomeUiState.kt └── HomeViewModel.kt
Each of these files will have the placeholders replaced with the correct feature and package names. The generated files will be ready to use, and you can implement your feature logic in them.
Customizations
1. Modify Templates
You can modify the files in the featureTemplate folder to suit your app’s structure and naming conventions.
2. Add More Templates
You can create additional template folders inside featureTemplate (e.g.,
repositoryTemplate,
serviceTemplate) and add new file generation logic in the Gradle task.
3. Placeholder Customization
The placeholders like {{packageName}},
{{featureName}}, and
{{className}} can be customized in your templates to support more dynamic replacements.
Benefits of Using Gradle for Feature Generation
- Efficiency: Automate repetitive tasks and focus on building features.
- Consistency: Maintain a standardized file structure and naming conventions across your project.
- Scalability: Easily add more templates or customize the Gradle task for additional features.
- Flexibility: Dynamically generate files using placeholders and templates tailored to your needs.
Conclusion
Automating feature creation with Gradle tasks can significantly streamline your development workflow, especially in projects with repetitive patterns. By defining templates and customizing them dynamically, you can maintain consistency, reduce errors, and save valuable development time.
This approach is particularly useful for scaling projects and ensuring that all modules adhere to your project’s structure and standards. Whether you’re generating screens, ViewModels, or other components, this method empowers you to focus more on logic and functionality rather than boilerplate code.
Note: If you need an example, check out my repository Screen Module Generator, where I’ve implemented a complete example to showcase an e-commerce product list feature using this Gradle task. It demonstrates how the entire structure is automatically set up and customized for practical use cases.
Happy coding!
Example GitHub Repo:
If you’re interested in learning more about Kotlin Multiplatform and Compose Multiplatform, check out my playlist on YouTube Channel:
Mastering Kotlin Multiplatform with Jetpack Compose: Complete Guide in Hindi
Thank you for reading! 🙌🙏✌ I hope you found this guide useful.
Don’t forget to clap 👏 to support me and follow for more insightful articles about Android Development, Kotlin, and KMP. If you need any help related to Android, Kotlin, and KMP, I’m always happy to assist.
Explore More Projects
If you’re interested in seeing full applications built with Kotlin Multiplatform and Jetpack Compose, check out these open-source projects:
- News Kotlin Multiplatform App (Supports Android, iOS, Windows, macOS, Linux):
News KMP App is a Kotlin Compose Multiplatform (KMP) project that aims to provide a consistent news reading experience across multiple platforms, including Android, iOS, Windows, macOS, and Linux. This project leverages Kotlin’s multiplatform capabilities to share code and logic while using Compose for UI, ensuring a seamless and native experience on each platform.
GitHub Repository: News-KMP-App - Gemini AI Kotlin Multiplatform App (Supports Android, iOS, Windows, macOS, Linux, and Web):
Gemini AI KMP App is a Kotlin Compose Multiplatform project designed by Gemini AI where you can retrieve information from text and images in a conversational format. Additionally, it allows storing chats group-wise using SQLDelight and KStore, and facilitates changing the Gemini API key.
GitHub Repository: Gemini-AI-KMP-App
Follow me on
Medium , YouTube , GitHub , Instagram , LinkedIn , Buy Me a Coffee , Twitter , DM Me For Freelancing Project
This article is previously published on proandroiddev.com.