KtLint: Linter for a Kotlin Multiplatform project

I use Kotlin Multiplatform with Gradle for one of my Compose Desktop developer productivity projects, maxDEV.

As the codebase grows, I aim to maintain a consistent coding style for the project. After a quick check, I decided to use the KtLint linter for this project.

KtLint provides various features, including a checker, formatter, Git hooks, and an IntelliJ plugin. It also supports an extra set of rules, such as the Jetpack Compose Ruleset, precisely what I needed for this project.

I will provide details of the configuration in this tutorial.

Gradle Plugin

I keep version and dependency reference in the project's libs.versions.toml file:

[versions]
ktlint-gradle = "12.2.0"

[plugins]
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }

Add dependency in the root build.gradle.kts:

plugins {
    // other plugins
    alias(libs.plugins.ktlint) apply false
}

And, update the build.gradle.kts for the desktop package:

plugins {
    // other plugins
    alias(libs.plugins.ktlint)
}

ktlint {
    verbose.set(true)
    outputToConsole.set(true)
    coloredOutput.set(true)
}

As you can see, I have only a few custom configurations for ktlint at this stage. You can also put this configuration in the root build settings if you have multiple multiplatform targets (iOS, Android).

Compose Ruleset

In addition to the standard rules, I also added Jetpack Compose Rules:

Using with ktlint - Jetpack Compose Rules
Compose Rules is a set of custom ktlint and detekt rules to ensure that your composables do not fall into common pitfalls

Reference and version in libs.versions.toml:

[versions]
ktlint-gradle = "11.6.1"
ktlint-ruleset-compose = "0.4.22"

[libraries]
ktlint-ruleset-compose = { module = "io.nlopez.compose.rules:ktlint", version.ref = "ktlint-ruleset-compose" }

[plugins]
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
💡
Please note that I had to fallback to ktlint-gradle 11.6.1 version so that Compose Ruleset works correctly.

Check and Format

We can use the following Gradle tasks to execute the linter:

./gradlew ktlintCheck
./gradlew ktlintFormat

In some cases, you might need to execute the clean task before running the linter:

./gradlew clean ktlintCheck

In addition, for my Kotlin Multiplatform project, I had to include some exclude rules for the generated files:

ktlint {
    verbose.set(true)
    outputToConsole.set(true)
    coloredOutput.set(true)

    filter {
        exclude("**/build/generated/**")
        exclude { element -> element.file.path.contains("resourceGenerator") }
    }
}

Ktlint CLI

You can also install Ktlint as a CLI tool and directly call it:

brew install ktlint

Add a pre-commit or pre-push GIT hook

ktlint installGitPreCommitHook
ktlint installGitPrePushHook

IntelliJ Plugin

Since my primary IDE is JetBrains IntelliJ, I also installed the ktlint-intellij-plugin:

Ktlint - IntelliJ IDEs Plugin | Marketplace
Formats code with KtLint after IntelliJ IDEA formatting or on save of file. Ktlint is an anti bikeshedding linter/formatter for Kotlin code based on the Kotlin Coding…

I have the following configuration for the plugin: