Android

Blog do Desenvolvedor Android: O que há de novo na versão de agosto '26 do Jetpack Compose

A versão 1.12 do Jetpack Compose foi lançada em agosto de 2026, trazendo novos recursos visuais como Mesh Gradients e suporte para cores amplas (Wide Color Gamut), além de melhorias na integração com o Android Credential Manager e otimizações de teste e desempenho.

Compartilhar
Android Developers Blog: What's new in the Jetpack Compose August '26 release

Hoje, a versão estável da atualização de agosto de 2026 do Jetpack Compose foi lançada! Esta release traz a versão 1.12 para módulos principais do Compose (confira o mapeamento completo BOM), apresentando APIs visuais ricas como Gradientes de Malha e suporte a Gamut de Cores Largas (WCG), recursos de layout estruturais como áreas nomeadas em Grid, integração sem interrupções com o Gerenciador de Credenciais do Android, além de melhorias significativas na testagem e desempenho.

Para atualizar seu projeto para esta release, é necessário atualizar a versão do Compose BOM para 2026.08.00:

implementation(platform("androidx.compose:compose-bom:2026.08.00"))

Mudanças que Quebram a Compatibilidade

AGP & Compile SDK: A versão 1.12 do Compose atualiza compileSdk para API 37, exigindo uma versão mínima de AGP 9.2.0. Como lembrete, o Compose sempre irá alvo a última compileSdk. Saiba mais sobre essa mudança aqui.

Modifier.onFirstVisible() está obsoleto: Migrar para Modifier.onVisibilityChanged(), que oferece um rastreamento mais preciso da barreira de visibilidade.

Gráficos

Gradientes de Malha

A versão 1.12 do Compose introduz MeshGradientPainter, para ajudar a criar gradientes de cor multi-pontos e orgânicos.

val rows = 1
val columns = 1

val gradientPainter = remember {
    MeshGradientPainter(rows, columns) {
        // Parâmetros: linha, coluna, posição, cor
        setVertex(0, 0, Offset(0f, 0f), Color.Red)     // Canto Superior Esquerdo
        setVertex(0, 1, Offset(1f, 0f), Color.Blue)    // Canto Superior Direito
        setVertex(1, 0, Offset(0f, 1f), Color.Green)   // Canto Inferior Esquerdo
        setVertex(1, 1, Offset(1f, 1f), Color.Yellow)  // Canto Inferior Direito
    }
}

Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

Para mais informações e exemplos, consulte a documentação.

Suporte ao Gamut de Cores Largas (WCG) & HDR

Displays modernos oferecem fidelidade de cor estendida e maior gama dinâmica. Na versão 1.12 do Compose, o suporte completo para Gamut de Cores Largas (P3) e renderização HDR foi habilitado em todo o pipeline gráfico do Compose, incluindo gráficos, pintura e shaders. Cores definidas em espaços de cor não-sRGB (como Display P3) são preservadas até a renderização da plataforma sem acanhamento de cores. As cores cairão seguramente para sRGB se usarem um espaço de cor não suportado (por exemplo, CieXyz, CieLab ou Oklab), dependendo em um espaço de cor em uma versão do Android não suportada (por exemplo Bt2020Hlg no Android 13 e abaixo) ou se o aplicativo está rodando em Android 9 (API 28) e abaixo.

Outras mudanças notáveis:

  • LayerOutsets foi adicionado a GraphicsLayer & Modifier.graphicsLayer, que você pode usar para aumentar os limites visuais da camada além de seu tamanho medido. Aplicar LayerOutsets evita o comportamento implícito clipToBounds quando a camada é promovida para um buffer offscreen.

Estilos

No Google I/O, compartilhamos nossa visão inicial da API de Estilo do Compose — uma maneira unificada e performática de estilizar componentes. Desde então, continuamos a construir a arquitetura subjacente para garantir segurança de tipo estrita e correção previsível, além de suportar o desenvolvimento de sistemas de design personalizados.

Para garantir que esta camada fundamental esteja correta, a API permanecerá experimental, e você pode esperar mudanças quebradoras.

Otimizações em Tempo de Execução

Sobrecarga Keyed SideEffect

Agora, o SideEffect suporta argumentos de chave, permitindo disparar efeitos de uma só vez sempre que chaves específicas mudam. Isso pode levar a um melhor desempenho em comparação com o uso de LaunchedEffect ou DisposableEffect quando você não precisa do bloco coroutine ou dispose. O SideEffect é até 90% mais rápido que o LaunchedEffect e cerca de 20% mais rápido que o DisposableEffect. Note que o SideEffect executa seu efeito antes do DisposableEffect e LaunchedEffect, então use cautela ao migrar efeitos existentes para esta API, especialmente para os LaunchedEffects que dependem de serem enviados para começar após a conclusão do quadro atual.

@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
    SideEffect(key1 = userId, key2 = screenName) {
        analytics.logScreenView(userId, screenName)
    }
}

Animação

O DeferredTargetAnimation saiu do status experimental.

Transições Interativas em Dois Níveis

Novos composables: DeferredAnimatedContent e DeferredAnimatedVisibility permitem criar transições agradáveis em dois níveis, por exemplo, para o rastreamento de gestos back preditivos.

Controle manual da animação: Durante a fase diferida da transição, as propriedades animadas (como escala ou deslocamento) podem agora ser manipuladas manualmente em tempo real (por exemplo, acompanhando um gesto de arrasto).

Transferência suave: Uma vez que a fase diferida termina, o motor de transição assume o controle e realiza uma transferência suave para a transição automática, incluindo a transferência da velocidade.

Suporte a elementos compartilhados: Um novo permitTransformDuringDeferredTransition em SharedContentConfig controla se os elementos compartilhados visualmente transformam junto com seus contêineres pais durante a fase de transição diferida.

val state = remember { DeferredTransitionState(initialScreen) }
val transition = rememberDeferredTransition(state)

if (predictiveBackInProgress) {
    state.defer(targetScreen)
} else {
    state.animateTo(targetScreen)
}

transition.DeferredAnimatedContent(
    targetState = targetScreen,
    mutableTransformSpec = {
       MutableContentTransform {
           // Manipule manualmente as propriedades durante a fase diferida
           initialContentTransform { scale = swipeProgress }
       }
    }
) { screen ->
    ScreenContent(screen)
}

Abaixo estão dois exemplos de casos de uso onde uma animação impulsionada por gestos é transferida para uma animação acionada:

Têxto, Entrada e Integrações com Plataforma

Formatação de Texto Editável

Novos APIs oferecem formatação de texto rico para têxtos editáveis em BasicTextField. Agora é possível aplicar e manipular formatos de caractere e parágrafo usando SpanStyle e ParagraphStyle via o novo método addStyle() dentro do escopo de um TextFieldBuffer (como dentro de textFieldState.edit { ... } ou uma InputTransformation). Além disso, o TextFieldBuffer fornece APIs como getSpanStyles() e getParagraphStyles(), que retornam objetos TrackedRange, permitindo ler, atualizar ou remover estilos aplicados. Para complementar a criação de formatações, o TextFieldState agora expõe uma propriedade somente leitura chamada textStyles para consultar estilos ativos em intervalos específicos, enquanto o TextFieldBuffer fornece originalTextStyles para inspecionar o estado de formatação antes de uma edição. A formatação e as anotações personalizadas são mantidas ao longo das alterações de configuração.

val state = rememberTextFieldState("Texto formatado no Compose 1.12")

// Aplicar estilos em negrito e cor a um intervalo de texto
state.edit {
    addStyle(
        SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
        start = 0,
        end = 9
    )
}

// Consultar estilos ativos do TextFieldState
val currentStyles = state.textStyles

Seleção de Texto

A nova API SelectionState fornece controle e observabilidade programáticos sobre a seleção de texto dentro de um SelectionContainer. Elevando um objeto SelectionState via rememberSelectionState() e passando para o SelectionContainer, a propriedade selectedTexts é exposta como uma lista reativa de AnnotatedStrings e métodos como selectAll(), clear(), select(TextRange) e extendSelectionByWord() são fornecidos.

Ainda, use getSelectableTexts() para recuperar todos os itens de texto selecionáveis em ordem de layout e selecione o texto entre composáveis no SelectionContainer usando um intervalo global.

@Composable
fun ProgrammaticSelectionExample() {
    val selectionState = rememberSelectionState()

    Column {
        Button(
            onClick = { selectionState.selectAll() },
            modifier = Modifier.disableSelectionClearOnTap()
        ) {
            Text("Selecionar Tudo")
        }

        SelectionContainer(state = selectionState) {
            Text("Conteúdo de texto a ser selecionado programaticamente.")
        }
    }
}

Integração com Gerenciador de Credenciais

Agora os campos de texto do Compose integram-se nativamente com o Gerenciador de Credenciais do Android (API 34+) via o framework Autofill (abaixo da API 34 é tratado pelo androidx.credentialslibrary). Ao anexar a nova propriedade semântica credentialRequest com um objeto CredentialRequestData, os campos de entrada podem solicitar passkeys, credenciais salvas ou pedidos de login diretamente no fluxo de entrada do usuário.

@Composable
fun LoginField(textFieldState: TextFieldState) {
    val credentialData = remember {
        CredentialRequestData(
            // Specify Credential Manager request options
        )
    }

    BasicTextField(
        state = textFieldState,
        modifier = Modifier.semantics {
            credentialRequest = credentialData
        }
    )
}

Outras mudanças notáveis:

  • Suporte para configurações de variação de fonte em fontes baixadas.
  • Habilitou a rolagem automática quando arrastar a seleção de texto além da viewport no componente SelectionContainer.
  • Adicionado suporte para sons automáticos (cliques e navegação de foco) em componentes Compose, com um novo componente SoundEffectOnInteraction para permitir a exclusão. Note que como resultado desta mudança, os ouvintes de semântica de clique devem agora ser chamados da thread principal, o que pode afetar um pequeno número de casos de teste.
  • KeyboardType agora inclui Date, Time, DateTime e SignedDecimal.
  • BasicSecureTextField agora usa TextObfuscationMode.System por padrão, enquanto RevealLastTyped serve como uma exclusão absoluta.

Aprimoramentos de Layout

Áreas Nomeadas em Layout Grid

Criar layouts 2D complexos agora é mais fácil com áreas nomeadas no componente @Experimental Grid. Em vez de gerenciar índices numéricos de coluna e linha entre itens, você pode definir regiões semânticas em seu GridConfigurationScope e posicionar composáveis por nome de área.

@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
    Grid(
        config = {
            area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
            area("sidebar", row = 1, column = 0)
            area("content", row = 1, column = 1)
            gap(16.dp)
        }
    ) {
        HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
        NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
        MainContentView(modifier = Modifier.gridItem(areaId = "content"))
    }
}

Desempenho

Como em cada lançamento, continuamos a investir no desempenho do Compose para garantir que o framework ajude você a construir aplicativos bonitos e performantes. Neste lançamento, focamos na melhoria da performance de inicialização e agora estamos vendo um tempo de exibição inicial (o tempo necessário para que um aplicativo produza seu primeiro quadro) comparável ao Views em nossos benchmark.

Atualizações de Testes e Ferramentas

Sincronização de Teste

A versão 1.12 do Compose introduz novos APIs de teste projetados para reduzir o tempo de execução dos testes e eliminar a instabilidade durante a amostragem de estado:

  • hasPendingWork: Verifica passivamente se o UI tem trabalho pendente sem avançar o relógio, ideal para loops de animação manual.

  • runWithoutImplicitWait: Desativa temporariamente a sincronização implícita ao passar por quadros de relógio manual (por exemplo, testes de animação).

    @Test
    fun testAnimationStateFast() {
    
    composeTestRule.mainClock.autoAdvance = false
        
    while (composeTestRule.hasPendingWork()) {
        composeTestRule.mainClock.advanceTimeByFrame()
        composeTestRule.waitForIdle()
        
        composeTestRule.runOnUiThread {
            composeTestRule.runWithoutImplicitWait {
                // Isso é mais eficaz quando consultando múltiplos nós em um único quadro.
                // Isso previne a sobrecarga de sincronização redundante que
                // ocorreria de outra forma em cada consulta individual.
                val box1 = composeTestRule.onNodeWithTag("Box1").fetchSemanticsNode()
                val box2 = composeTestRule.onNodeWithTag("Box2").fetchSemanticsNode()
                
                assertThat(box1.boundsInRoot.right).isAtMost(box2.boundsInRoot.left)
            }
        }
    }
    }

    Outras alterações notáveis:

    • A API captureToImage agora permite capturar um popup ou diálogo junto com seu âncora em uma única imagem.
    • Foi adicionado onRootWithViewInteraction para escopar pesquisas semânticas do Compose a específicos Views Android. Isso simplifica o teste de UIs híbridas, como RecyclerViews, sem exigir tags de teste únicas no código de produção.
    • Anotações @PreviewWrapper podem agora ser aplicadas a classes personalizadas @MultiPreview, habilitando configurações de visualização reutilizáveis (como temas personalizados) em vários componentes.

    Sinta-se à vontade para criar!

    Como sempre, valorizamos sua opinião, então compartilhe seu feedback sobre essas alterações ou o que gostaria de ver a seguir no nosso issue tracker. Sinta-se à vontade para criar!

  • Fonte original

    Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

    Leia a matéria completa