A release de abril de 2026 do Jetpack Compose trouxe algo que muitos desenvolvedores Android e multiplataforma pediam havia tempo: componentes de layout de verdade para interfaces adaptativas. Grid, FlexBox e Media Query API chegaram como APIs experimentais e prometem mudar a forma como construímos telas responsivas em Kotlin.

Se você trabalha com Jetpack Compose no dia a dia, sabe que montar layouts complexos com Row e Column aninhados rapidamente vira uma dor de cabeça. Breakpoints manuais, cálculos de largura e lógica condicional espalhada por todo o Composable tornam o código difícil de manter. Os três novos componentes resolvem exatamente isso.

Neste artigo, vamos explorar cada API com exemplos práticos e prontos para copiar.

O que mudou no Compose em abril de 2026?

A atualização trouxe três APIs experimentais que seguem padrões já consolidados no desenvolvimento web:

  • Grid: layout bidimensional com linhas, colunas e spans
  • FlexBox: distribuição flexível de itens com wrap e alinhamento em múltiplos eixos
  • Media Query API: adaptação declarativa baseada em sinais do ambiente como tamanho de janela, postura do dispositivo e tipo de input

Além dessas, a release também incluiu uma nova Styles API para estilização baseada em estado e melhorias no suporte a trackpad. Mas o foco deste artigo são os três componentes de layout, que impactam diretamente a arquitetura de qualquer app adaptativo.

Se você já trabalha com Compose Multiplatform, essas APIs funcionam também no Desktop e Web, tornando ainda mais viável compartilhar código de UI entre plataformas.

Grid: layouts bidimensionais de verdade

O componente Grid resolve o problema mais comum de quem precisa montar dashboards, galerias ou qualquer tela com disposição em grade. Antes, era preciso combinar LazyVerticalGrid com cálculos manuais. Agora, a API oferece controle total sobre tracks (linhas e colunas), gaps e spans.

Exemplo básico: grade de cards

@OptIn(ExperimentalComposeApi::class)
@Composable
fun DashboardGrid() {
    Grid(
        config = {
            columns(4) { fraction(0.25f) }
            rows(2) { fraction(0.5f) }
            gap(16.dp)
        }
    ) {
        MetricCard(
            titulo = "Receita",
            valor = "R$ 42.500",
            modifier = Modifier.gridItem(rowSpan = 2)
        )
        MetricCard(
            titulo = "Usuarios",
            valor = "1.230",
            modifier = Modifier.gridItem(columnSpan = 2)
        )
        MetricCard(titulo = "Pedidos", valor = "89")
        MetricCard(titulo = "Conversao", valor = "3,2%")
    }
}

O Grid aceita diferentes unidades de dimensionamento: Dp fixo, frações (Fr), porcentagens e até tamanhos intrínsecos baseados no conteúdo. Isso permite criar grades que se adaptam naturalmente ao espaço disponível sem cálculos manuais.

Exemplo avançado: adaptação por orientação

@OptIn(ExperimentalComposeApi::class)
@Composable
fun AdaptiveProductGrid(produtos: List<Produto>) {
    val configuration = LocalConfiguration.current
    val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

    Grid(
        config = {
            if (isLandscape) {
                columns(4) { fraction(0.25f) }
            } else {
                columns(2) { fraction(0.5f) }
            }
            row { auto() }
            gap(horizontal = 12.dp, vertical = 16.dp)
        }
    ) {
        produtos.forEach { produto ->
            ProductCard(
                produto = produto,
                modifier = if (produto.destaque) {
                    Modifier.gridItem(columnSpan = 2)
                } else {
                    Modifier
                }
            )
        }
    }
}

Quem já construiu telas com layouts em Compose vai perceber que o Grid elimina boa parte da lógica condicional que antes era necessária para responsividade.

FlexBox: distribuição inteligente de itens

O FlexBox segue a mesma filosofia do CSS Flexbox: distribuir itens dentro de um container com controle sobre direção, wrap, alinhamento e proporção. A grande vantagem sobre Row e Column é o suporte nativo a wrap — quando os itens não cabem em uma linha, eles quebram automaticamente para a próxima.

Exemplo: barra de tags com wrap

@OptIn(ExperimentalComposeApi::class)
@Composable
fun TagBar(tags: List<String>) {
    FlexBox(
        config = {
            wrap(FlexWrap.Wrap)
            gap(8.dp)
            justifyContent(JustifyContent.Start)
            alignItems(AlignItems.Center)
        }
    ) {
        tags.forEach { tag ->
            TagChip(
                text = tag,
                modifier = Modifier.flex { shrink(0f) }
            )
        }
    }
}

@Composable
fun TagChip(text: String, modifier: Modifier = Modifier) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(16.dp),
        color = MaterialTheme.colorScheme.secondaryContainer
    ) {
        Text(
            text = text,
            modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
            style = MaterialTheme.typography.labelMedium
        )
    }
}

Exemplo: layout de formulário adaptativo

@OptIn(ExperimentalComposeApi::class)
@Composable
fun AdaptiveForm() {
    FlexBox(
        config = {
            wrap(FlexWrap.Wrap)
            gap(horizontal = 16.dp, vertical = 12.dp)
        }
    ) {
        OutlinedTextField(
            value = "",
            onValueChange = {},
            label = { Text("Nome") },
            modifier = Modifier.flex {
                grow(1f)
                basis(200.dp)
            }
        )
        OutlinedTextField(
            value = "",
            onValueChange = {},
            label = { Text("Email") },
            modifier = Modifier.flex {
                grow(2f)
                basis(300.dp)
            }
        )
        OutlinedTextField(
            value = "",
            onValueChange = {},
            label = { Text("Telefone") },
            modifier = Modifier.flex {
                grow(1f)
                basis(150.dp)
            }
        )
    }
}

O basis define o tamanho mínimo preferido antes da distribuição do espaço extra via grow. Isso garante que os campos nunca fiquem menores do que o necessário, mas expandem proporcionalmente quando há espaço sobrando. Para quem já usou Kotlin DSL em outros contextos, a API do FlexBox segue o mesmo padrão de configuração declarativa.

Media Query API: adaptação baseada no ambiente

A Media Query API é possivelmente a adição mais poderosa desta release. Ela permite que seu Composable reaja declarativamente a sinais do ambiente sem precisar de lógica imperativa espalhada pelo código.

Sinais disponíveis

A API reconhece diversos sinais ambientais:

  • Tamanho da janela: largura e altura em WindowSizeClass (compact, medium, expanded)
  • Postura do dispositivo: flat, half-opened, tabletop (para dobráveis)
  • Tipo de teclado: virtual ou físico
  • Precisão do ponteiro: touch ou mouse
  • Orientação: portrait ou landscape

Exemplo: layout responsivo com Media Query

@OptIn(ExperimentalComposeApi::class)
@Composable
fun ResponsiveHomeScreen(viewModel: HomeViewModel) {
    val showSidebar = mediaQuery {
        windowWidthSizeClass >= WindowWidthSizeClass.Medium
    }

    val isTabletop = mediaQuery {
        windowPosture == UiMediaScope.Posture.Tabletop
    }

    if (isTabletop) {
        TabletopLayout(viewModel)
    } else if (showSidebar) {
        Row {
            NavigationSidebar(modifier = Modifier.width(280.dp))
            MainContent(
                viewModel = viewModel,
                modifier = Modifier.weight(1f)
            )
        }
    } else {
        Column {
            MainContent(
                viewModel = viewModel,
                modifier = Modifier.weight(1f)
            )
            BottomNavigation()
        }
    }
}

Performance com derivedMediaQuery

Para sinais que mudam com alta frequência, a API oferece derivedMediaQuery, que otimiza recomposições usando derivedStateOf internamente:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun AdaptiveGrid(items: List<Item>) {
    val columnCount = derivedMediaQuery {
        when {
            windowWidthSizeClass == WindowWidthSizeClass.Expanded -> 4
            windowWidthSizeClass == WindowWidthSizeClass.Medium -> 3
            else -> 2
        }
    }

    Grid(
        config = {
            columns(columnCount) { fraction(1f / columnCount) }
            gap(12.dp)
        }
    ) {
        items.forEach { item ->
            ItemCard(item)
        }
    }
}

Essa composição entre Grid e Media Query API mostra o verdadeiro poder das novas APIs trabalhando juntas. Se você já lida com observabilidade em apps Kotlin, vale monitorar o impacto de recomposições usando essas ferramentas.

Combinando Grid, FlexBox e Media Query

Na prática, as três APIs se complementam. Um cenário comum seria:

@OptIn(ExperimentalComposeApi::class)
@Composable
fun ProductCatalogScreen(produtos: List<Produto>, categorias: List<String>) {
    val isExpanded = mediaQuery {
        windowWidthSizeClass == WindowWidthSizeClass.Expanded
    }

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        // Tags de filtro com wrap automático
        FlexBox(
            config = {
                wrap(FlexWrap.Wrap)
                gap(8.dp)
            }
        ) {
            categorias.forEach { cat ->
                FilterChip(selected = false, onClick = {}, label = { Text(cat) })
            }
        }

        Spacer(modifier = Modifier.height(16.dp))

        // Grade adaptativa de produtos
        Grid(
            config = {
                if (isExpanded) {
                    columns(4) { fraction(0.25f) }
                } else {
                    columns(2) { fraction(0.5f) }
                }
                gap(12.dp)
            }
        ) {
            produtos.forEach { produto ->
                ProductCard(produto)
            }
        }
    }
}

Como habilitar as novas APIs

Todas as APIs desta release são experimentais. Para usá-las, adicione a dependência mais recente do Compose BOM e a anotação @OptIn:

// build.gradle.kts
dependencies {
    implementation(platform("androidx.compose:compose-bom:2026.04.00"))
    implementation("androidx.compose.foundation:foundation")
    implementation("androidx.compose.foundation:foundation-layout")
}

No código, use @OptIn(ExperimentalComposeApi::class) em cada Composable que utilize Grid, FlexBox ou Media Query.

Se você está configurando um projeto do zero, nosso tutorial de Compose com Android Studio cobre o setup inicial completo. Para projetos multiplataforma, o guia de Compose Multiplatform Desktop mostra como compartilhar código de UI entre Android e Desktop.

Quando usar cada componente

CenárioComponente
Dashboard com cards em gradeGrid
Barra de tags ou chips com quebra de linhaFlexBox
Formulário com campos de tamanho variávelFlexBox
Layout completamente diferente por tamanho de telaMedia Query
Galeria de imagens com itens em destaqueGrid + Media Query
Adaptação para dispositivos dobráveisMedia Query

Conclusão

Grid, FlexBox e Media Query API representam a maior evolução de layout do Jetpack Compose desde o lançamento do framework. Juntas, elas eliminam a necessidade de gambiarras com Row/Column aninhados e lógica condicional espalhada pelo código.

Se você já domina os fundamentos do Compose, é hora de experimentar essas APIs em protótipos. Elas ainda são experimentais, mas a direção é clara: o Compose está se tornando um framework de layout tão poderoso quanto CSS Grid e Flexbox na web, com a vantagem de tipagem estática e integração nativa com o ecossistema Kotlin.

Para quem trabalha com Kotlin Multiplatform, a boa notícia é que essas APIs fazem parte do Compose Foundation, disponível para Android, Desktop e Web. Combine isso com Navigation 3 e Hot Reload e você tem um toolkit completo para aplicações modernas. No ecossistema web, Go tem explorado abordagens como HTMX para UIs reativas no servidor, mas o Compose oferece uma experiência declarativa nativa que dispensa JavaScript no client. Linguagens como Rust com egui também investem em UI declarativa, mostrando que esse paradigma está se consolidando além do ecossistema Android.

Se você está migrando de XML para Compose, nosso guia de Jetpack Compose completo cobre a transição passo a passo. E para entender como a nova desestruturação por nome do Kotlin pode simplificar seu código de UI com data classes, confira o artigo complementar.


Perguntas frequentes

O Grid do Compose substitui LazyVerticalGrid?

Não diretamente. O Grid é voltado para layouts fixos e responsivos com controle de tracks e spans. O LazyVerticalGrid continua sendo a melhor opção para listas longas com reciclagem de itens. Use Grid para dashboards e layouts de página, e LazyVerticalGrid para feeds e catálogos com scroll infinito.

FlexBox funciona em Compose Multiplatform?

Sim. O FlexBox faz parte do foundation-layout, que está disponível em todas as plataformas suportadas pelo Compose Multiplatform: Android, Desktop (JVM), iOS e Web (via Kotlin/Wasm). A API é a mesma em todas as plataformas.

A Media Query API recompoe muito e prejudica performance?

A API foi projetada com otimização em mente. Ela usa derivedStateOf internamente para sinais de alta frequência, e o derivedMediaQuery permite controle fino sobre quando recomposições acontecem. Na prática, a performance é comparável a verificar LocalConfiguration.current manualmente.

Preciso de alguma versão mínima do Android para usar Grid e FlexBox?

As APIs experimentais do Compose seguem o suporte padrão do Compose Foundation, que funciona a partir do Android API 21 (Lollipop). A Media Query API para detecção de postura em dobráveis requer a biblioteca WindowManager da Jetpack e dispositivos compatíveis.