Using with ktlint
Using with Kotlinter¶
If using kotlinter, you can specify the dependency on this set of rules by using the buildscript
classpath.
buildscript {
dependencies {
classpath "io.nlopez.compose.rules:ktlint:<version>"
}
}
Using with ktlint-gradle¶
Note: You need at least version 11.1.0 of this plugin.
If using ktlint-gradle, you can specify the dependency on this set of rules by using the ktlintRuleset
.
dependencies {
ktlintRuleset "io.nlopez.compose.rules:ktlint:<VERSION>"
}
Using with spotless¶
See Spotless Ktlint Integration.
Using with ktlint CLI or the ktlint IntelliJ plugin¶
The releases page contains an uber jar for each version release that can be used for these purposes. In the releases page you can identify them by the suffix -all.jar
.
To use with ktlint CLI:
ktlint -R ktlint-compose-<VERSION>-all.jar
You can use this same uber jar from the releases page with the ktlint IntelliJ plugin if the rules are compiled against the same ktlint version used for that release. You can configure the custom ruleset in the preferences page of the plugin.
Supported versions matrix¶
Version | Ktlint version | Kotlin (Syntax) |
---|---|---|
0.4.18+ | 1.4.1 | 2.0.21 |
0.4.17 | 1.4.0 | 2.0.21 |
0.4.12+ | 1.3.1 | 2.0.20 |
0.4.6+ | 1.3.1 | 2.0.0 |
0.4.5 | 1.3.0 | 2.0.0 |
0.3.12+ | 1.2.1 | 1.9.23 |
0.3.9+ | 1.1.1 | 1.9.22 |
Older version support can be found in the release notes.
Configuring rules¶
Providing custom content emitters¶
There are some rules (compose:content-emitter-returning-values-check
, compose:modifier-not-used-at-root
and compose:multiple-emitters-check
) that use predefined list of known composables that emit content. But you can add your own too! In your .editorconfig
file, you'll need to add a compose_content_emitters
property followed by a list of composable names separated by commas. You would typically want the composables that are part of your custom design system to be in this list.
[*.{kt,kts}]
compose_content_emitters = MyComposable,MyOtherComposable
Providing exceptions to content emitters¶
Sometimes we'll want to not count a Composable towards the multiple content emitters (compose:multiple-emitters-check
) rule. This is useful, for example, if the composable function actually emits content but that content is painted in a different window (like a dialog or a modal). For those cases, we can use a denylist compose_content_emitters_denyylist
to add those composable names separated by commas.
[*.{kt,kts}]
compose_content_emitters_denylist = MyModalComposable,MyDialogComposable
Providing custom ViewModel factories¶
The vm-injection-check
rule will check against common ViewModel factories (eg viewModel
from AAC, weaverViewModel
from Weaver, hiltViewModel
from Hilt + Compose, etc), but you can configure your .editorconfig
file to add your own, as a list of comma-separated strings:
[*.{kt,kts}]
compose_view_model_factories = myViewModel,potatoViewModel
Providing a list of allowed CompositionLocal
s¶
For compositionlocal-allowlist
rule you can define a list of CompositionLocal
s that are allowed in your codebase.
[*.{kt,kts}]
compose_allowed_composition_locals = LocalSomething,LocalSomethingElse
Ignore annotated functions with specific annotations for missing Modifier checks¶
In the modifier-missing-check
rule, you can define a list of annotations that, if present, will make it so the function is exempt from this rule.
[*.{kt,kts}]
compose_modifier_missing_ignore_annotated = Potato,Banana
Allowing matching function names¶
The naming-check
rule requires all composables that return a value to be lowercased. If you want to allow certain patterns though, you can configure a comma-separated list of matching regexes in your .editorconfig
file:
[*.{kt,kts}]
compose_allowed_composable_function_names = .*Presenter,.*SomethingElse
Allowing custom state holder names¶
The vm-forwarding-check
rule will, by default, design as a state holder any class ending on "ViewModel" or "Presenter". You can, however, add new types of names to the mix via a comma-separated list of matching regexes in your .editorconfig
file:
[*.{kt,kts}]
compose_allowed_state_holder_names = .*ViewModel,.*Presenter,.*Component,.*SomethingElse
Allowlist for composable names that aren't affected by the ViewModelForwarding rule¶
The vm-forwarding-check
will catch VMs/state holder classes that are relayed to other composables. However, in some situations this can be a valid use-case. The rule can be configured so that all the names of composables that match a list of regexes are exempt to this rule. You can configure this in your .editorconfig
file:
[*.{kt,kts}]
compose_allowed_forwarding = .*Content,.*SomethingElse
Allowlist for ViewModel/state holder names that aren't affected by the ViewModelForwarding rule¶
The vm-forwarding-check
will catch VMs/state holder classes that are relayed to other composables. However, in some situations this can be a valid use-case. The rule can be configured so that all the names of ViewModels that match a list of regexes are exempt to this rule. You can configure this in your .editorconfig
file:
[*.{kt,kts}]
compose_allowed_forwarding_of_types = .*MyViewModel,PotatoViewModel
Configure the visibility of the composables where to check for missing modifiers¶
The modifier-missing-check
rule will, by default, only look for missing modifiers for public composables. If you want to lower the visibility threshold to check also internal compoosables, or all composables, you can configure it in your .editorconfig
file:
[*.{kt,kts}]
compose_check_modifiers_for_visibility = only_public
Possible values are:
only_public
: (default) Will check for missing modifiers only for public composables.public_and_internal
: Will check for missing modifiers in both public and internal composables.all
: Will check for missing modifiers in all composables.
Configure custom Modifier names¶
Most of the modifier-related rules will look for modifiers based their type: either Modifier or GlanceModifier type. Some libraries might add their own flavor of Modifier to the mix, and it might make sense to enforce the same rules we have for the other default modifiers. To support that, you can configure this in your .editorconfig
file:
[*.{kt,kts}]
compose_custom_modifiers = BananaModifier,PotatoModifier
Configure types to treat as lambdas (e.g. for ParamOrder check)¶
The param-order-check
rule will do its best to identify trailing lambdas. However, in cases where a typedef / functional interface is being used, we might want to have this rule to treat them as if they were lambdas: not reporting them if they are the last in a method signature and they don't have a default value. To give ktlint some hints, you can configure this in your .editorconfig
file:
[*.{kt,kts}]
compose_treat_as_lambda = MyLambdaType,MyOtherLambdaType
Configure types to treat as composable lambdas (e.g. for ContentTrailingLambda check)¶
The content-trailing-lambda
rule will do its best to identify @Composable
trailing lambdas. However, in cases where a typedef / functional interface is being used, we might want to have this rule to treat them as if they were composable lambdas: not reporting them if they are the last in a method signature and they don't have a default value. To give ktlint some hints, you can configure this in your .editorconfig
file:
[*.{kt,kts}]
compose_treat_as_composable_lambda = MyLambdaComposableType,MyOtherComposableLambdaType
Enabling the Material 2 detector¶
The material-two
rule will flag any usage of a Material 2 API. This rule is disabled by default, so you'll need to explicitly enable it in your .editorconfig
file:
[*.{kt,kts}]
compose_disallow_material2 = true
You might also want to disallow Material 2, but allow a specific set / subset of APIs. The rule allows this too.
For example, let's say you want to allow all filled icons (whose fully qualified names are androidx.compose.material.icons.filled.*) and the Button
composable (androidx.compose.material.Button). This is how you'd allow those:
[*.{kt,kts}]
compose_disallow_material2 = true
compose_allowed_from_m2 = icons.filled,Button
Enabling the unstable collections detector¶
The unstable-collections
rule will flag any usage of any unstable collection (e.g. List/Set/Map). This rule is disabled by default, so you'll need to explicitly enable it in your .editorconfig
file:
[*.{kt,kts}]
compose_disallow_unstable_collections = true
Enabling and configuring the preview naming detector¶
If you want to enforce a naming strategy for previews, you can enable the compose:preview-naming
rule and configure the compose_preview_naming_strategy
property in your .editorconfig
file:
[*.{kt,kts}]
compose_preview_naming_enabled = true
compose_preview_naming_strategy = suffix
Possible values of compose_preview_naming_strategy
are:
suffix
: Previews should havePreview
as suffix.prefix
: Previews should havePreview
as prefix.anywhere
: Previews should containPreview
in their names.
Disable standard:function-naming
rule for Composable¶
The function name for a Composable starts with an uppercase. This causes the ktlint rule standard:function-naming
to report a violation. This rule can be configured to ignore Composable functions in your .editorconfig
file:
[*.{kt,kts}]
ktlint_function_naming_ignore_when_annotated_with = Composable
Disabling a specific rule¶
To disable a rule you have to follow the instructions from the ktlint documentation, and use the id of the rule you want to disable with the compose
tag.
For example, to disable the naming-check
rule, the tag you'll need to disable is ktlint:compose:naming-check
.
@Suppress("ktlint:compose:naming-check")
fun YourComposableHere() { ... }