---
title: "Configurador de Estabilidade do Compose: guia completo com Kotlin em 2026 | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/configurador-estabilidade-compose-kotlin-2026/"
markdown_url: "https://kotlin.dev.br/blog/configurador-estabilidade-compose-kotlin-2026.MD"
description: "Domine o Configurador de Estabilidade (Stability Configurator) do Jetpack Compose com Kotlin em 2026: classes estáveis vs instáveis, @Immutable, @Stable, arquivo de configuração de estabilidade, diagnóstico com Compose Compiler Metrics e benchmarks de recomposição."
date: "2026-06-21"
author: "Karina Melo"
---

# Configurador de Estabilidade do Compose: guia completo com Kotlin em 2026 | Kotlin Brasil

Domine o Configurador de Estabilidade (Stability Configurator) do Jetpack Compose com Kotlin em 2026: classes estáveis vs instáveis, @Immutable, @Stable, arquivo de configuração de estabilidade, diagnóstico com Compose Compiler Metrics e benchmarks de recomposição.


Recomposição desnecessária é o problema de performance número um em apps Jetpack Compose em produção. O sintoma é clássico: a UI engasga, lista trava em scroll, mas o profiler não mostra nada óbvio — porque a causa raiz raramente é "código lento" e quase sempre é o **Compose Compiler classificando como instável uma classe que deveria ser estável**. Em 2026, com o Compose Compiler 1.6+ integrado ao Kotlin 2.2+, o **Configurador de Estabilidade** (Stability Configurator) e o arquivo `stability_config.conf` se tornaram a ferramenta oficial para corrigir esse problema sem espalhar anotações pelo código.

Neste guia você configura o estabilidade do Compose corretamente, do diagnóstico ao fix, com Kotlin 2.2 e Compose BOM 2026. Se você ainda não dominou os fundamentos de recomposição, vale revisar antes o [guia de Jetpack Compose](/guias/guia-jetpack-compose/) e o [guia de performance em Kotlin](/guias/guia-kotlin-performance/), porque este artigo pressupõe que você já sabe o que é slot table e quando o Compose decide recompor.

## 1. Por que estabilidade importa

O Compose decide se um composable vai recompor comparando os parâmetros da chamada atual com a anterior. Para isso funcionar de forma barata, o compilador marca cada parâmetro como **estável** ou **instável**:

- **Estável**: o tipo pode ser comparado por igualdade estrutural e o Compose confia que, se os parâmetros não mudaram, a recomposição é segura de pular.
- **Instável**: o Compose não consegue provar que o valor não mudou, então recomputa sempre.

A consequência prática é direta: um único parâmetro instável em um composable grande força a recomposição inteira dele e de todos os filhos que não estiverem protegidos por uma chave explícita. Em uma lista com 50 itens, isso vira jank visível.

A regra clássica do compilador é simples, mas restritiva:

- Tipos primitivos (`Int`, `String`, `Boolean`) são estáveis.
- Tipos `val`-only com campos estáveis e declarados como `data class` **são** estáveis.
- Tipos com pelo menos um `var` ou com um campo de tipo instável (como `List`, `Set`, `Map` da `kotlin.collections`) **são marcados como instáveis**, mesmo que você nunca os muta.

É essa última regra que causa a maioria dos problemas reais. Uma `data class` imutável que contém `List<String>` é instável para o compilador, porque `List` é uma interface que pode ter uma implementação mutável (`MutableList`) por baixo.

## 2. Diagnóstico: ativando o relatório de estabilidade

Antes de mexer em nada, ative o relatório do Compose Compiler para ver exatamente quais classes estão instáveis e quais composables estão recompondo demais. No `build.gradle.kts` do módulo:

```kotlin
android {
    composeCompiler {
        // Kotlin 2.2+: use a extensão composeCompiler do plugin
        reportsDestination = layout.buildDirectory.dir("compose_compiler")
        metricsDestination = layout.buildDirectory.dir("compose_compiler")
    }
}
```

Após um `./gradlew :app:assembleDebug`, abra `app/build/compose_compiler/*-classes.txt`. Cada classe aparece assim:

```
stable class MinhaDataClassImutavel {
  val nome: String
  val idade: Int
}
unstable class MinhaListaClass {
  val itens: List<String>   // <-- instável porque List é instável
}
```

O arquivo `*-composables.txt` mostra, para cada composable, se ele é `restartable`, `skippable` e quais parâmetros são instáveis. Um composable `restartable` mas **não** `skippable` com parâmetro instável é o seu alvo de otimização.

## 3. Solução clássica: @Immutable e @Stable

A correção mais antiga (e ainda válida) é anotar a classe. O [artigo sobre data classes e sealed classes em Kotlin](/blog/sealed-classes-kotlin/) cobre o design delas em profundidade; aqui o foco é a anotação de estabilidade.

```kotlin
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable

@Immutable
data class EstadoTela(
    val itens: List<String>,        // agora tratada como estável
    val selecionado: Boolean
)

@Stable
class ViewModelHolder(val estado: StateFlow<EstadoTela>)
```

A diferença entre as duas anotações:

- `@Immutable`: você garante que **nenhum** campo muda após a construção. Se você mentir (um `var` mutável por baixo), o Compose vai exibir UI inconsistente. Use só em classes realmente imutáveis.
- `@Stable`: a classe pode mudar, mas você garante que notifica o Compose (`MutableState`, `StateFlow` coletado como `State`, etc.). É o caso típico de wrappers de `ViewModel`.

O problema dessa abordagem é que ela **espalha dependência do `androidx.compose.runtime`** por módulos que não deveriam conhecer Compose (camada de domínio, de dados). É aí que entra o Configurador de Estabilidade.

## 4. Configurador de Estabilidade (stability_config.conf)

Desde o Compose Compiler 1.5.4 (e refinado até o 1.6.x de 2026), você pode declarar tipos estáveis em um arquivo de configuração, sem anotar o código-fonte. Crie `compose-stability.conf` na raiz do módulo:

```ini
// Considera estas classes estáveis para o Compose, sem anotação no código
java.time.Duration
java.time.Instant
java.time.LocalDate
java.util.UUID
kotlin.coroutines.CoroutineContext

// Pacote inteiro da camada de modelo pode ser marcado como estável
com.meuapp.modelo.*
```

E referencie no `build.gradle.kts`:

```kotlin
android {
    composeCompiler {
        stabilityConfigurationFile = project.layout.projectDirectory.file("compose-stability.conf")
    }
}
```

As regras aceitas no arquivo são:

- **Nome totalmente qualificado** de uma classe: `com.meuapp.modelo.Usuario`.
- **Pacote curinga**: `com.meuapp.modelo.*` (cobre todos os tipos do pacote, recursivamente em subpacotes com `com.meuapp.modelo.**`).
- Comentários com `#` ou `//`.

Isso resolve o caso mais comum: você tem uma camada de modelo `data class` pura (sem `var`, sem `MutableList`) que o compilador marca como instável só porque contém `List`. Marcando o pacote como estável, todo o modelo passa a ser skippable sem poluir o código de domínio com anotação Android.

## 5. Caso prático: lista que travava no scroll

Considere um caso real. Você tem:

```kotlin
data class Produto(
    val id: String,
    val nome: String,
    val tags: List<String>   // <-- torna a classe instável
)

@Composable
fun ListaProdutos(produtos: List<Produto>) {
    LazyColumn {
        items(produtos, key = { it.id }) { produto ->
            ItemProduto(produto)   // recomputa a cada scroll por causa do tipo instável
        }
    }
}

@Composable
fun ItemProduto(produto: Produto) {
    // ...
}
```

Sem correção, o relatório mostra `ItemProduto` como `restartable` mas não `skippable`. A correção mínima é adicionar ao `compose-stability.conf`:

```ini
com.meuapp.modelo.*
```

Rode novamente o build, regenere o relatório e confirme que `ItemProduto` agora aparece como `skippable`. O efeito em uma lista de 50 itens costuma ser uma queda de 30 a 60% no tempo de recomposição medido pelo [Macrobenchmark](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/), porque o Compose passa a pular recomposições de itens que não mudaram.

## 6. Armadilhas comuns

### 6.1. List, Set, Map da `kotlin.collections`

Mesmo com `@Immutable` na classe que as contém, o compilador em algumas versões ainda reclama. A forma mais robusta em 2026 é usar o configurador no pacote do modelo, ou converter para tipos imutáveis do KotlinX (`ImmutableList`, `PersistentList` via `kotlinx.collections.immutable`), que o compilador reconhece como estáveis nativamente.

```kotlin
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf

data class Carrinho(val itens: ImmutableList<Produto> = persistentListOf())
```

### 6.2. Classes com `List` mutada internamente

Não minta para o compilador. Se você marcar uma classe como `@Immutable` mas mutar a lista por referência externa, o Compose não vai recompor quando deveria e a UI fica dessincronizada. Nesse caso, use `@Stable` com `MutableState` ou migre para `ImmutableList`.

### 6.3. Lambdas capturando estado

Um composable com parâmetro lambda `(Produto) -> Unit` é estável, mas se o lambda captura um `var` externo, a instabilidade volta pela porta dos fundos. Use `remember { { produto -> ... } }` ou extraia o estado para um `rememberSaveable`. Isso conecta com o padrão discutido no [guia de Flow e StateFlow](/blog/kotlin-flow/) — o estado observável sempre deve vir de uma fonte estável.

### 6.4. ViewModel como parâmetro

Passar o `ViewModel` inteiro como parâmetro de composable deixa tudo instável. Extraia o estado em uma `data class` imutável e observe com `collectAsStateWithLifecycle()`. Veja o [guia de arquitetura MVVM em Kotlin](/guias/guia-arquitetura-mvvm-kotlin/) para o padrão de UI state.

## 7. Medindo o ganho com Macrobenchmark

Depois de aplicar o configurador, meça com Macrobenchmark para confirmar o ganho real, não só o relatório do compilador. Um benchmark de scroll típico:

```kotlin
@Test
fun scrollListaProdutos() = benchmarkRule.measureRepeated(
    packageName = "com.meuapp",
    metrics = listOf(FrameTimingMetric())
) {
    startActivityAndWait()
    val lista = device.findObject(By.res("lista_produtos"))
    device.scrollUntilFound(lista, UiScrollable.Direction.DOWN, 20)
}
```

Compare o `frameDurationCpuMs` P95 antes e depois. Reduções de 8-10ms para 4-5ms são comuns quando a causa raiz era instabilidade de parâmetro. Combine com o [guia de Baseline Profiles](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/) para o ganho cumulativo.

## 8. Checklist final

Antes de fechar um PR de otimização de estabilidade:

1. Rode `./gradlew :app:assembleDebug` e abra `*-classes.txt` — confirme que as classes-alvo viraram `stable`.
2. Abra `*-composables.txt` — confirme que os composables afetados viraram `skippable`.
3. Escreva um Macrobenchmark de scroll e compare P95 de `frameDurationCpuMs`.
4. Verifique que nenhum `@Immutable` está mentindo (grep por `var` em classes anotadas).
5. Documente no `compose-stability.conf` por que cada pacote foi marcado — um comentário por linha economiza debug futuro.

## Conclusão

O Configurador de Estabilidade é o investimento de maior ROI em performance de Compose para apps médios e grandes em 2026. Ele resolve na origem o problema de recomposição desnecessária sem acoplar a camada de domínio ao runtime do Compose, e o ganho é mensurável com as ferramentas que você já tem no build. A regra prática: se o relatório do compilador mostra uma `data class` pura marcada como `unstable`, o `stability_config.conf` é quase sempre a resposta certa.

Para aprofundar, combine este guia com o de [Baseline Profiles e Macrobenchmark](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/), o de [Material 3 Expressive](/blog/material-3-expressive-compose-kotlin-2026/) (que também depende de parâmetros estáveis para animar bem) e o [guia de testes em Kotlin](/guias/guia-testes-kotlin/) para validar o comportamento da UI após a mudança.
