Introduction
Kotlin Multiplatform (KMP) revolutionizes cross-platform development by letting you share up to 90% of your Kotlin code across Android, iOS, desktop (JVM), web (JS), and server. Unlike transpiling frameworks like Flutter, KMP compiles natively for each target, delivering optimal performance and full access to native APIs.
Why is it essential in 2026? Hybrid apps are booming, but code duplication kills maintenance efficiency. With KMP, centralize your business logic (validation, calculations, data models) in a shared module, and handle platform specifics via expect/actual.
This beginner tutorial walks you through creating a minimal project: a shared platformName() function runnable on JVM (desktop console) and compilable for Android. At the end, you'll have a fully functional project ready to expand to iOS. Estimated time: 15 minutes. Think of it like a common skeleton with sport-specific muscles.
Prerequisites
- IntelliJ IDEA Community (free) or Android Studio (for optional Android emulator)
- JDK 21 installed (
sdk install java 21-temvia SDKMAN or official download) - Gradle 8.10+ (used via wrapper, no manual install needed)
- Terminal (bash/zsh on Mac/Linux, Git Bash/PowerShell on Windows)
- Basic Kotlin knowledge (functions, packages)
Initialize the project structure
mkdir my-kmp-project
cd my-kmp-project
gradle wrapper --gradle-version 8.10.2
mkdir -p shared/src/commonMain/kotlin/org/example
mkdir -p shared/src/jvmMain/kotlin/org/example
mkdir -p shared/src/androidMain/kotlin/org/exampleThis script creates the root folder, initializes the Gradle wrapper for reproducibility, and sets up the KMP source set hierarchy: commonMain for shared code, jvmMain for desktop, androidMain for mobile. The org.example packages avoid conflicts. Run it in a clean terminal; the Gradle wrapper downloads everything automatically.
Configure settings.gradle.kts
This file centralizes module and repository management. It includes the shared module and sets up repositories for Kotlin/Gradle. Open it in IntelliJ (File > Open > my-kmp-project) for syntax highlighting.
settings.gradle.kts (root)
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "my-kmp-project"
include(":shared")Configures plugins and repositories to avoid Android/Kotlin conflicts. FAIL_ON_PROJECT_REPOS enforces centralized usage, reducing resolution errors. include(":shared") declares the shared module. Copy-paste as-is; optimized for Kotlin 2.0+.
Configure root build.gradle.kts
The root build defines global options like the JVM toolchain. It applies the KMP plugin without loading it here (delegated to the module).
build.gradle.kts (root)
kotlin {
jvmToolchain(21)
}
allprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = "21"
}
}
}Sets JDK 21 globally via jvmToolchain for multiplatform consistency. Applies jvmTarget = "21" to all Kotlin compiles, avoiding JVM warnings. Minimal yet robust; extend for more modules.
Configure the shared module
KMP core: This build targets JVM (console executable) and Android (library). It links the source sets. Create shared/build.gradle.kts.
build.gradle.kts (shared)
plugins {
kotlin("multiplatform")
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
androidTarget()
jvm {
compilations.all {
kotlinOptions.jvmTarget = "21"
}
withJava()
}
sourceSets {
commonMain { }
androidMain { }
val jvmMain by getting { }
}
}
tasks.register<org.gradle.process.JavaExec>("run") {
group = "run"
classpath = sourceSets["jvmMain"].runtimeClasspath
mainClass.set("org.example.MainKt")
standardInput = System.`in`
}Applies KMP plugin, targets androidTarget() and jvm() for two platforms. Empty source sets for minimal start (add deps later). run task executes JVM main via dynamic classpath. Pitfall: Without withJava(), no JAR access.
Define the shared function (expect)
expect declares the common API. Implement actual per platform. Create the files at the specified paths.
Platform.kt (commonMain)
package org.example
expect fun platformName(): StringDeclares expect fun: shared signature, compiled everywhere. Kotlin ensures each platform has a matching actual. Simple as a contract; extend to classes, suspend functions, or generics.
PlatformJvm.kt (jvmMain)
package org.example
actual fun platformName(): String {
return "JVM (Desktop)"
}actual implements for JVM. Returns static string; for dynamic, use System.getProperty("os.name"). Must match expect signature exactly, or link error.
PlatformAndroid.kt (androidMain)
package org.example
actual fun platformName(): String {
return "Android"
}actual for Android: Access Build.MODEL if needed after importing android.os.Build. Compiles to reusable AAR. Pitfall: No JVM APIs here (no native Android System props).
Add the main entry point
To test on JVM, add a main() that consumes shared code. Use it as a library in a real Android app.
main.kt (jvmMain)
package org.example
fun main() {
println("Bonjour depuis ${platformName()} !")
println("Votre premier projet KMP fonctionne parfaitement.")
}Top-level main() calls shared platformName(). Runs via ./gradlew run. In Android, import org.example.platformName in Activity/ViewModel. Works without classes; scales to complex apps.
Compile and test
Open the project in IntelliJ/Android Studio (auto-import Gradle). Check: No red errors.
Build and run
cd my-kmp-project
./gradlew clean build
./gradlew run
# Vérifiez Android compile
./gradlew compileKotlinAndroidclean build compiles all targets (JVM JAR + Android classes). run launches console: "Bonjour depuis JVM (Desktop) !". compileKotlinAndroid validates Android without emulator. Success = KMP ready! Time: <1min.
Best practices
- Always use expect/actual for platform-specific APIs (files, networking); keep pure functions in commonMain.
- Add shared deps (
kotlinx.coroutinesin commonMain) for async code. - Use
compose.multiplatformfor shared UI after this basics. - Pin Kotlin/Gradle versions; test multi-OS via Gradle checks.
- Structure: models/data in common, UI/impls per platform.
Common errors to avoid
- No actual: Link-time "Unsatisfied expectation" error. Check source set paths.
- Wrong jvmTarget: JDK mismatch causes NoClassDefFound. Force 21 everywhere.
- Missing repositories: Add google() for Android deps; snapshots for Kotlin nightly.
- Wrong MainClass: Use
MainKtfor top-level (not "main"). Debug with--stacktrace.
Next steps
- Official docs: Kotlin Multiplatform docs
- Add iOS:
iosX64()+ Xcode integration. - Real project: Integrate in Android Studio (New > KMP Shared Module).
- Master it with our Learni Dev courses: Advanced KMP, Compose Multiplatform.
- GitHub template: Fork this project.