Jetpack Compose Compiler Report Reveals Stability Misclassification in Complex UI Trees
0 reputation · 05 Sept 2025, 02:47 UTC
0 reputation · 05 Sept 2025, 02:47 UTC
Jetpack Compose's stability system determines whether a Composable can skip recomposition by analyzing parameter stability. Parameters marked as unstable always trigger recomposition of the parent Composable, even when the underlying data hasn't meaningfully changed. This becomes problematic in complex UI trees where multiple nested composables receive parameters from external libraries or classes containing var properties.
The compiler treats classes from external libraries or those with mutable state as unstable by default because it cannot guarantee immutability or proper change notification. While @Stable and @Immutable annotations allow manual marking, misapplication can introduce UI bugs if the stability contract isn't genuinely followed. Additionally, standard Kotlin List and Map interfaces result in instability since they don't guarantee immutability, requiring Kotlinx collections instead.
The Compose compiler report is the primary diagnostic tool for identifying non-skippable composables and their unstable parameters, enabled through Gradle build configuration. However, the report alone doesn't indicate whether the instability classification is correct or if manual annotations would be appropriate.
29275 reputation · 05 Sept 2025, 06:37 UTC
At what point should developers trust the compiler’s default stability classification versus manually annotating classes with @Stable or @Immutable?
val properties, and uses immutable collections, the compiler’s inference is usually safe.var fields, is open, or uses mutable Kotlin List/Map, trust the compiler’s default of unstable unless you can prove the library guarantees immutability.@Immutable only for data classes that are final, all properties are val, and use Kotlinx immutable collections.@Stable for types that expose mutable state but guarantee that any mutation will trigger recomposition (e.g., via MutableState).var, no open classes, no mutable collections.-Pplugin:androidx.compose.compiler.plugins.kotlin:stabilityReport=true) and inspect the Stability section for non‑skippable composables. @Stable/@Immutable on classes with var, open declarations, or mutable collections.build.gradle.kts:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Pplugin:androidx.compose.compiler.plugins.kotlin:stabilityReport=true"
}
compose-stability-report.html.var fields or mutable collections?@Immutable (or @Stable if shallow stability suffices). If no, wrap the type in an internal immutable façade or keep it unannotated.To refine the recommendation, could you share which external library types are being passed to your composables? Knowing the exact classes would help determine whether a façade or a manual annotation is appropriate.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.