Create Custom New Android Compose Project Template For Android Studio

Here I will show you how to create a new project template for Android Studio / IntelliJ Idea CE.

I wanted to create this template because I wanted to add the Compose Compiler to new projects. This prevents me from having to manually update the references each time.

You will need to create a plugin for Android Studio using IntelliJ Idea CE ( Community Edition ), it is free. You can download the IDE from https://www.jetbrains.com/idea/download/

After you download and install the IDE, you will need to get the base plugin, IntelliJ Platform Plugin. You can get it from https://github.com/JetBrains/intellij-platform-plugin-template. This plugin is a template that is a good starting point to create your own. Alternatively, you could use the New Project Wizard in IntelliJ Idea CE to create the plugin from scratch.

File >> New >> Project…

Here you would select just as the image shows.

IDE Plugin with type as Plugin

For the rest of this post I will show you how I used the template from Github.

You can either clone the repository to your Github or download the zip. I chose to download the zip in case I needed to go back to the original multiple times ( which I did ).

At the Github template site, you can read to directions on how to configure the template. They seem straightforward but I still had issues trying to make it all work. I also referenced the IntelliJ Platform Gradle Plugin Site. This is useful but to me was missing some straightforward directions out of the box for my use case.

I read through many posts online but had issues due to the posts were comprised of using the IntelliJ Platform Gradle Plugin (1.0). The link above shows directions with the new IntelliJ Platform Gradle Plugin (2.0). The old version is not supported on the newer builds of Android Studio I was using so I needed to build the plugin using the newer versions.

Below are my steps:

1. Get Template

As I stated above I chose to download the zip. I then extracted the contents to a folder in the root of my IdeaProjects folder. This may be different for everyone but is defaulted to:

C:\Users\<USERNAME>\IdeaProjects\

2. Create Project For Plugin

There I opened the project

File >> Open >> Select the directory of the extracted folder. For the rest of this walkthrough, I changed the root folder of the project to test before I opened it in IntelliJ Idea CE.

3. Edit build.gradle

Now open the

build.gradle.kts file

Here I changed the JVM target from

kotlin {
    jvmToolchain(17)
}

to

kotlin {
    jvmToolchain(21) // 17
}

4. Referencing IDE Android Plugin for build.gradle

Now is the part that you need to do a little research on the plugin of Android you have installed on your IDE. You will need to reference the plugin to the release chart at Android Studio Plugin Development

These are my steps.

Open the IDE you are using and open the top toolbar

Navigate to Help >> About

Android Studio

IntelliJ Idea CE

You will need to look at the build version and the IDE version. For Android Studio, reference the releases link above to get the appropriate information for your build.

You may also go top the current plugin page of the IDE you are using and check the build version there. For IntelliJ I need to go there for the version of Android that is used.

File >> Settings >> Plugins

As shown above, for either IDE, you will need Android plugin ( obviously already installed on Android Studio ) as well as the Plugin Devkit. The android plugin version number here is shown from my IntelliJ IDE.

I will need to specify for IntelliJ Idea CE -> org.jetbrains.android:243.22562.218

Or for my Android Studio -> org.jetbrains.android:242.23339.11

Now with the information you need, you can move on by adding the plugin with version in the build.gradle file. I added this after the repository block.

repositories {
    mavenCentral()

    // IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html
    intellijPlatform {
        defaultRepositories()
    }
}

dependencies {
    intellijPlatform {
        plugin("org.jetbrains.android:243.22562.218")
    }
}

5. Edit gradle.properties

Now you will open the gradle.properties file and make a few changes

This is the default template

# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html

pluginGroup = org.jetbrains.plugins.template
pluginName = IntelliJ Platform Plugin Template
pluginRepositoryUrl = https://github.com/JetBrains/intellij-platform-plugin-template
# SemVer format -> https://semver.org
pluginVersion = 2.0.2

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 233
pluginUntilBuild = 242.*

# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = IC
platformVersion = 2023.3.8

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP
platformPlugins =
# Example: platformBundledPlugins = com.intellij.java
platformBundledPlugins =

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 8.10.2

# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
kotlin.stdlib.default.dependency = false

# Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html
org.gradle.configuration-cache = true

# Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html
org.gradle.caching = true

You will need to change:

  • pluginGroup – change to what you want your structure to be
  • pluginName – change to your own plugin name
  • pluginSinceBuild – first set of digits from the android plugin version used
  • pluginUntilBuild – first set of digits from the android plugin version used end along with .*
  • platformType – IC = IntelliJ Idea CE IDE used or AI is Android Studio, leave as IC if doing plugin for both
  • platformVersion – version of the IDE. Use the version found from step #4 above
  • platformPlugins – org.jetbrains.android:< version found in step#4 >
  • platformBundledPlugins – com.intellij.java, org.jetbrains.kotlin
  • remove both gradle cache entries

Below is what I changed mine to

# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html

pluginGroup = com.template.r2creations24.testingtemplate
pluginName = Testing Template
pluginRepositoryUrl = https://github.com/JetBrains/intellij-platform-plugin-template
# SemVer format -> https://semver.org
pluginVersion = 2.0.2

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 243
pluginUntilBuild = 243.*

# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = IC
platformVersion = 2024.3.1

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP
platformPlugins = org.jetbrains.android:243.22562.218
# Example: platformBundledPlugins = com.intellij.java
platformBundledPlugins = com.intellij.java, org.jetbrains.kotlin

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 8.10.2

# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
kotlin.stdlib.default.dependency = false

6. Edit settings.gradle.kts

Now open the settings.gradle.kts file

Change the rootProject.name to what you put in pluginName in step #5.

7. Refactor Template Namespace

Now we will refactor the project path as the pluginGroup that we entered in step #5

In the Project window on the left pane, expand until you expose the org.jetbrains.plugins.template and change to what you entered as pluginGroup.

8. Edit plugin.xml

Now we will edit the plugin.xml file. It is located at:

src >> main >> kotlin >> resources >> META-INF >>

Change the name tag to the pluginName you entered in step #5

Then we will need to add a few dependencies here as well as the “New Project Wizard” reference.

Add the below tags under the com.intellij.modules.platform depends tag

***********com.intellij.modules.androidstudio**************

if you set the platform as IC for IntelliJ IdeaCE in step #5, remove this tag. If using Android Studio and you set the platform to AI above, then keep.

    <depends>org.jetbrains.android</depends>
    <depends>org.jetbrains.kotlin</depends>
    <depends>com.intellij.modules.java</depends>
    <depends>com.intellij.modules.androidstudio</depends>

Then add the following extension tag suited to your namespace

    <extensions defaultExtensionNs="com.android.tools.idea.wizard.template">
        <wizardTemplateProvider implementation="com.template.r2creations24.testingtemplate.MyProjectTemplatesProvider"/>
    </extensions>

9. Wizard Template

Now lets create the MyProjectTemplatesProvider referencing in the previous step.

I added mine to the root of the pluginGroup name as you see in the implementation reference statement in step #8. You can put it whereever you want just make sure to update the reference.

Create file MyProjectTemplatesProvider.kt

import com.android.tools.idea.wizard.template.Template
import com.android.tools.idea.wizard.template.WizardTemplateProvider
import com.template.r2creations24.testingtemplate.emptyActivity.*

class MyProjectTemplatesProvider : WizardTemplateProvider() {

    override fun getTemplates(): List<Template> {
        return listOf(projectTemplate)
    }
}

10. Create Package For Template Classes

Now the initial setup is done.

The next 3 files I have declared here are created from the post I saw here to help me create this plugin.

https://sasikanth.dev/creating-project-templates-in-android-studio

We just need to create the empty activity for us to use as well as the way for the WizardTemplateProvider to get its reference to it.

I created a new package off of the pluginGroup com.template.r2creations24.testingtemplate name emptyActivity to hold the next few classes.

Right-click on the pluginGroup in the Project window pane on the left.

New >> Package >> give it a name ( mine is emptyActivity )

11. Create Template Files

Create a new file in this package. name ProjectTemplate.kt and add the below code

import com.android.tools.idea.wizard.template.*
import com.android.tools.idea.wizard.template.Constraint.*
import com.android.tools.idea.wizard.template.impl.defaultPackageNameParameter
import java.io.File

val projectTemplate
    get() = template {
        name = "Empty Activity"
        description = "Create a new empty activity with Jetpack Compose"
        minApi = 24
        constraints = listOf(
            TemplateConstraint.AndroidX, TemplateConstraint.Kotlin, TemplateConstraint.Material3,
            TemplateConstraint.Compose)

        category = Category.Compose
        formFactor = FormFactor.Mobile
        screens = listOf(WizardUiContext.ActivityGallery, WizardUiContext.MenuEntry, WizardUiContext.NewProject, WizardUiContext.NewModule, WizardUiContext.NewProjectExtraDetail)

        val activityClass = stringParameter {
            name = "Activity Name"
            default = "MainActivity"
            help = "The name of the activity class to create"
            constraints = listOf(CLASS, UNIQUE, NONEMPTY)
            //loggable = true
        }

        val packageName = defaultPackageNameParameter

        val isLauncher = booleanParameter {
            name = "Launcher Activity"
            default = true
            visible = { true }
            help = "If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it visible in the launcher"
        }

        val greeting = stringParameter {
            name = "Greeting function name"
            default = "Greeting"
            help = "Used for deduplication"
            visible = { false }
            constraints = listOf(UNIQUE, KOTLIN_FUNCTION)
            //loggable = true
        }

        val defaultPreview = stringParameter {
            name = "Default Preview function name"
            default = "${greeting.value}Preview"
            help = "Used for deduplication"
            visible = { false }
            constraints = listOf(UNIQUE, KOTLIN_FUNCTION)
            //loggable = true
        }

        val canAddComposeDependencies = booleanParameter {
            name = "Add Compose Dependencies"
            default = false
            visible = { true }
        }

        widgets(
            TextFieldWidget(activityClass),
            PackageNameWidget(packageName),
            CheckBoxWidget(isLauncher),
            CheckBoxWidget(canAddComposeDependencies),
            // Invisible widgets to pass data
            TextFieldWidget(greeting),
            TextFieldWidget(defaultPreview),
            LanguageWidget()
        )

        thumb { File("compose-activity-material3").resolve("template_compose_empty_activity_material3.png") }

        recipe = { data: TemplateData ->
            projectRecipe(
                data as ModuleTemplateData,
                activityClass.value,
                packageName.value,
                defaultPreview.value,
                greeting.value,
                isLauncher.value,
                canAddComposeDependencies = canAddComposeDependencies.value
            )
        }
    }

In the same package/directory add another file named ProjectRecipe.kt and add

import com.android.tools.idea.wizard.template.ModuleTemplateData
import com.android.tools.idea.wizard.template.PackageName
import com.android.tools.idea.wizard.template.RecipeExecutor
import com.android.tools.idea.wizard.template.impl.activities.common.addAllKotlinDependencies
import com.android.tools.idea.wizard.template.impl.activities.common.addLifecycleDependencies
import com.android.tools.idea.wizard.template.impl.activities.common.addMaterial3Dependency
import com.android.tools.idea.wizard.template.impl.activities.common.generateManifest

private const val COMPOSE_BOM_VERSION = "2024.12.01"
private const val COMPOSE_KOTLIN_COMPILER_VERSION = "1.5.1"

fun RecipeExecutor.projectRecipe(
    moduleData: ModuleTemplateData,
    activityName: String,
    packageName: PackageName,
    defaultPreview : String,
    greeting : String,
    isLauncher : Boolean,
    canAddComposeDependencies: Boolean
) {

    requireJavaVersion("1.8", true)
    setBuildFeature("compose", true)
    // Note: kotlinCompilerVersion default is declared in TaskManager.COMPOSE_KOTLIN_COMPILER_VERSION
    setComposeOptions(kotlinCompilerExtensionVersion = COMPOSE_KOTLIN_COMPILER_VERSION)
    addMaterial3Dependency()
    addLifecycleDependencies(true)
    addAllKotlinDependencies(moduleData)

    generateManifest(
        moduleData = moduleData,
        activityClass = activityName,
        activityThemeName = moduleData.themesData.main.name,
        packageName = packageName,
        isLauncher = isLauncher,
        hasNoActionBar = true,
        generateActivityTitle = true
    )

    applyPlugin("org.jetbrains.kotlin.plugin.compose", "2.0.0")

    if (canAddComposeDependencies) {
        addDependency(mavenCoordinate = "androidx.lifecycle:lifecycle-runtime-ktx:+")
        addDependency(mavenCoordinate = "androidx.activity:activity-compose:+")
        addPlatformDependency(mavenCoordinate = "androidx.compose:compose-bom:$COMPOSE_BOM_VERSION")
        addPlatformDependency(mavenCoordinate = "androidx.compose:compose-bom:$COMPOSE_BOM_VERSION", configuration = "androidTestImplementation")

        val composeUiFormattedVersion = moduleData.let { ":$it" } ?: ""
        addDependency(mavenCoordinate = "androidx.compose.ui:ui$composeUiFormattedVersion")
        addDependency(mavenCoordinate = "androidx.compose.ui:ui-graphics")
        addDependency(mavenCoordinate = "androidx.compose.ui:ui-tooling", configuration = "debugImplementation")
        addDependency(mavenCoordinate = "androidx.compose.ui:ui-tooling-preview")
        addDependency(mavenCoordinate = "androidx.compose.ui:ui-test-manifest", configuration="debugImplementation")
        addDependency(mavenCoordinate = "androidx.compose.ui:ui-test-junit4", configuration="androidTestImplementation")

        addDependency(mavenCoordinate = "androidx.compose.material3:material3")
    }



    val themeName = "${moduleData.themesData.appName}Theme"

    val emptyActivity = emptyActivity(packageName, activityName, defaultPreview, greeting, themeName)
    val emptyActivityPath = moduleData.srcDir.resolve("$activityName.kt")
    save(emptyActivity, emptyActivityPath)
    open(emptyActivityPath)
}

In the same package/directory add another file named EmptyActivity.kt and add the following

import com.android.tools.idea.wizard.template.escapeKotlinIdentifier

fun emptyActivity(
    packageName: String,
    activityClass: String,
    defaultPreview: String,
    greeting: String,
    themeName: String
) = """
package ${escapeKotlinIdentifier(packageName)}

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import ${escapeKotlinIdentifier(packageName)}.ui.theme.${themeName}

class $activityClass : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            $themeName {
                Scaffold( modifier = Modifier.fillMaxSize() ) { innerPadding ->
                    ${greeting}(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

@Composable
fun ${greeting}(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello ${"$"}name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun ${defaultPreview}() {
    $themeName {
        ${greeting}("Android")
    }
}
"""

12. Update libs.versions.toml

Update the kotlin and the intellij plugin to the latest version. Currently this is what I edited. Can be found in Project pane to the left

<projectname> >> gradle

intelliJPlatform = "2.2.1"
kotlin = "2.0.0"

13. Sync Gradle and Build Plugin

We are done. We can sync gradle and then build the plugin. Open the gradle pane in on the right toolbar. Click Sync. When done, then buildPlugin

The plugin will build and be inside your

C:\Users\<username>\IdeaProjects\test\build\libs\

Now you can go to your IDE settings and add the plugin.

File >> Settings >> Plugins ( left pane )

Then Install Plugin From Disk…

Hope this was helpful to anyone.

Issues I Had

I spent about a week trying to get this to work in Android Studio. It works in IntelliJ Idea CE no problem. In Android Studio, it installs and works fine unless if I tick the “Add Compose Depenedencies” in the creation wizard.

No matter what I changed, it kept giving me a failure pointing to the android plugin build couldn’t find the addDependency(“”) method.

From my research, it seems that org.jetbrains.android:242.23339.11 has a issue with the DependencyHelper.

I posted help

https://stackoverflow.com/questions/79291235/adddependency-issue-in-intellij-platform-plugin-for-android-studio-to-create-new

I also posted it https://intellij-support.jetbrains.com/hc/en-us/community/posts/23450869457042-Creating-an-Android-Studio-plugin-to-create-new-project-templates-NoMethodFound-addDependency

I am sharing in case if someone comes across same issue. I would prefer to just use 1 IDE but in the meantime this works on the other.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *