KMP (Kotlin Multiplatform) Windows Quick Start

Kotlin Multiplatform Windows Quick Start

Purpose

Quickly set up your first Kotlin Multiplatform project in a Windows environment. This should take around five to ten minutes to follow (mainly due to dependency downloads and compile time), but may take longer if internet / machine is slow or if you get lost with the steps.

This will not show you how to write an app with any real functionality outside of a button and an animated image (which is a part of the template you download).

Prerequisites

  • Experience with Android Development
  • Android Studio Android Studio Giraffe | 2022.3.1 or above (tested with Narwhal | 2025.1.1 and 2025.1.2)

Notes

  • This is very easy if you are used to Android Studio. You need to download a project template as a start point, but then the dependency issues can be confusing to start.
  • You will not be able to build and iOS version of your app without suitable Apple hardware, but you can still make and work on the project.
  • There is no option to create a Kotlin Multiplatform project on Windows (or Linux) by default (but I believe this exists on Apple machines).
    • This guide - https://www.jetbrains.com/help/kotlin-multiplatform-dev/quickstart.html#ds3xqs_48 - is a good reference for Apple users - thank you, Stevekitkat (https://medium.com/@stevekitkat) for the reference
  • This guide shows you how to avoid having to set Java environment settings specifically for KMP
  • It also covers updating to the latest dependencies as of the date of writing that will build and run correctly (full list at end of guide)
Current Windows Options:

Full Process to Build and Run on Android and Desktop

Step 1 - Download Template (a minute or so):

Go to kmp.jetbrains.com to create your template project.
  1. In New Project - Keep the default Project Name and Project ID for your first test
  2. Below, make sure
    1.  Android is selected, 
    2. iOS->UI Implementation->Share UI (with Compose Multiplatform UI framework)
    3. Desktop is selected
    4. Web is NOT selected (currently Alpha so leave this for the first test)
  3. Download the template

Step 2 - Extract and Copy (a few seconds):

Extract the project. You should end up with something like this in the extracted KotlinProject folder:

Copy (or move) the KotlinProject into your Android Studio Project Folder or perhaps a subfolder. Be sure you don't have an existing project with the same name first.

Step 3 - Open and Sync (around three minutes):

Open the project.

Select Trust Project (this code is coming from a reputable source, but of course, free free to open in preview mode first if you are worried).


This takes some time, but wait for the project to sync. This may take 30 seconds to a few minutes depending on your computer and internet connection. 

It will look like this when it is syncing...

 
... and like this when it is finished (but you may need to complete step 4 first).

Step 4  - Set SDK / Java Settings (a few seconds to a few minutes):

IMPORTANT: Ensure your SDK location is set. Without this, you will get errors about not being able to find the correct Java version and to set your system settings correctly.
  1. Open local.properties
  2. Ensure your directory is set correctly
    1. If it is blank, you can open an working Compose project and copy the location from there.
    2. Example: sdk.dir=X\:\\Android\\SDK 
    3. Note the double backslashes (\\) between folder and the drive letter.
  3. If you needed to change the SDK location, you should have to hit Sync Now for everything to work. This will take a bit of time to download all the required files.

Step 5 - Test Launch on Android (a minute or so):

Launch the project to make sure everything is working by hitting the Play Button.


Step 6 - Run on Desktop (a couple of minutes):

There are at least two ways to get it running on desktop:
  1. Run in Terminal - enter -> ./gradlew :composeApp:desktopRun
  2. Set up new configeration
    1. From the top bar, click the three dots near the Run button
    2. Select Edit
    3. Select Add a New Configeration
    4. Hit the Plus Nutton
    5. Select Gradle
    6. Change the Name to Desktop (or leave it the same if you wish)
    7. Change the Run line to :composeApp:run
    8. Click Ok
    9. The newly added config should now be selected (select it if not)
    10. Hit Run






Success!!!


Step 8 (Optional - Advanced) - Update Dependencies:

Update the following files if you want to run with all the latest improvements:
  1. `Open gradle-wrapper-properties and update to latest version
    • As of writing, tested upgrading 8.9 to 8.14.3:
      • distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
      • distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
  2. Open libs.versions.toml and update to latest versions.
    • Updates - I manually updated these / used the upgrade assistant for the agp upgrade:
      • agp = "8.11.0" (or 8.12.0)
      • composeHotReload = "1.0.0-beta03" (or beta05)
      • android-targetSdk = "36"
      • android-compileSdk = "36"
  3. Open build.gradle.kts (Modile :composeApp)
    1. In android, Comment out the compilerOptions from androidTarget (not needed as this is set later in compileOptions anyway).
    2. Also in android,  Update the compileOptions to VERSION_21 (Java 21). This should give performance increases and other advantages. If you have any compatibility issues, you can also drop it back to version 11.


Of course, Sync and Run again to make sure it is working after the update.

Finished!

That should be everything you need to start working with KMP. In a Windows (or Linux?) environment, you should able to launch the Android and Desktop versions of the App. Coming soon is the Web based version (currently in Alpha).

Remember - you need Apple hardware (for Xcode) to build an iOS version, but you can still work on shared code on other machines.

Links

Official Documentation

PL Coding YouTube - A great place for Android / Compose / KMP Tutorials

KMP Project Template (as linked earlier)

Dependencies (No need to read)

Here is a copy of dependencies working on my current machine as of publishing date working when created with Narwhal | 2025.1.1 (in case you accidently get stuck by making manual changes):

build.gradle.kts (Project: KotlinProject)

plugins {
    // this is necessary to avoid the plugins to be loaded multiple times
    // in each subproject's classloader
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.androidLibrary) apply false
    alias(libs.plugins.composeHotReload) apply false
    alias(libs.plugins.composeMultiplatform) apply false
    alias(libs.plugins.composeCompiler) apply false
    alias(libs.plugins.kotlinMultiplatform) apply false
}

build.gradle.kts (Project: KotlinProject)

import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidApplication)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
    alias(libs.plugins.composeHotReload)
}

kotlin {
    androidTarget {
//        @OptIn(ExperimentalKotlinGradlePluginApi::class)
//        compilerOptions {
//            jvmTarget.set(JvmTarget.JVM_11)
//        }
    }
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true
        }
    }
    
    jvm("desktop")
    
    sourceSets {
        val desktopMain by getting
        
        androidMain.dependencies {
            implementation(compose.preview)
            implementation(libs.androidx.activity.compose)
        }
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3)
            implementation(compose.ui)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
            implementation(libs.androidx.lifecycle.viewmodel)
            implementation(libs.androidx.lifecycle.runtimeCompose)
        }
        commonTest.dependencies {
            implementation(libs.kotlin.test)
        }
        desktopMain.dependencies {
            implementation(compose.desktop.currentOs)
            implementation(libs.kotlinx.coroutinesSwing)
        }
    }
}

android {
    namespace = "org.example.project"
    compileSdk = libs.versions.android.compileSdk.get().toInt()

    defaultConfig {
        applicationId = "org.example.project"
        minSdk = libs.versions.android.minSdk.get().toInt()
        targetSdk = libs.versions.android.targetSdk.get().toInt()
        versionCode = 1
        versionName = "1.0"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_21
        targetCompatibility = JavaVersion.VERSION_21
    }
}

dependencies {
    debugImplementation(compose.uiTooling)
}

compose.desktop {
    application {
        mainClass = "org.example.project.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "org.example.project"
            packageVersion = "1.0.0"
        }
    }
}

libs.versions.toml

[versions]
agp = "8.11.0"
android-compileSdk = "36"
android-minSdk = "24"
android-targetSdk = "36"
androidx-activity = "1.10.1"
androidx-appcompat = "1.7.1"
androidx-constraintlayout = "2.2.1"
androidx-core = "1.16.0"
androidx-espresso = "3.6.1"
androidx-lifecycle = "2.9.1"
androidx-testExt = "1.2.1"
composeHotReload = "1.0.0-beta03"
composeMultiplatform = "1.8.2"
junit = "4.13.2"
kotlin = "2.2.0"
kotlinx-coroutines = "1.10.2"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
junit = { module = "junit:junit", version.ref = "junit" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }

[plugins]

androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeHotReload = { id = "org.jetbrains.compose.hot-reload", version.ref = "composeHotReload" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

gradle-wrapper-properties

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Popular Posts