---
title: "Android 16 Edge-to-Edge com Kotlin e Compose | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/android-16-edge-to-edge-compose-kotlin-2026/"
markdown_url: "https://kotlin.dev.br/blog/android-16-edge-to-edge-compose-kotlin-2026.MD"
description: "Prepare apps Kotlin para Android 16: edge-to-edge obrigatório, WindowInsets no Compose, barras do sistema, teclado, listas e testes de regressão visual."
date: "2026-06-07"
author: "Karina Melo"
---

# Android 16 Edge-to-Edge com Kotlin e Compose | Kotlin Brasil

Prepare apps Kotlin para Android 16: edge-to-edge obrigatório, WindowInsets no Compose, barras do sistema, teclado, listas e testes de regressão visual.


O Android 16 torna impossível empurrar o problema de **edge-to-edge** para depois. Desde o Android 15, apps com `targetSdk` 35 já desenham por baixo das barras do sistema em muitos cenários. No Android 16, para apps mirando API 36, a rota de escape `windowOptOutEdgeToEdgeEnforcement` deixa de resolver o problema. Se a sua tela Compose ainda depende de `statusBarColor`, margens fixas ou `padding(top = 24.dp)`, a migração para `targetSdk = 36` pode revelar botões cobertos, listas cortadas, FAB colado na barra de navegação e campos de texto escondidos pelo teclado.

A boa notícia: em Kotlin com Jetpack Compose, a solução costuma ser limpa quando o time trata **WindowInsets** como parte da arquitetura de UI. Edge-to-edge não significa deixar tudo atrás da barra do sistema. Significa permitir que o app use a janela inteira e aplicar espaçamentos conscientes onde o conteúdo precisa permanecer legível, clicável e acessível.

Se você já acompanha nossos guias de [Jetpack Compose](/guias/guia-jetpack-compose/), [Navigation 3 no Android](/blog/navigation-3-compose-android-2026/), [Baseline Profiles e Macrobenchmark](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/) e [testes Android com Compose e Maestro](/guias/testes-android-compose-maestro/), este artigo fecha uma lacuna prática: preparar a UI para o comportamento visual que o Android moderno exige.

## O que muda no Android 16?

O ponto central é simples: apps que miram Android 16 precisam assumir que o conteúdo pode ocupar a área por trás de status bar, navigation bar e display cutouts. Em versões anteriores, alguns times mantinham a aparência antiga com flags de opt-out. Essa estratégia fica frágil quando o app avança o `targetSdk`.

Na prática, você deve revisar:

- telas com `Scaffold` e barras superiores;
- listas em `LazyColumn` ou `LazyVerticalGrid`;
- botões fixos no rodapé;
- formulários com teclado aberto;
- bottom sheets, dialogs e overlays;
- telas com imagens hero ou mapas;
- navegação com predictive back;
- flows de login, checkout, perfil e detalhes.

O erro mais comum é tratar insets como um ajuste cosmético. Eles são uma regra de layout. Se o app já usa Compose, estado de UI, coroutines e navegação declarativa, vale integrar insets ao mesmo nível de cuidado que você dá a temas, breakpoints e acessibilidade.

## Configure o projeto para target SDK 36

Antes de ajustar UI, deixe claro onde o projeto está. Um exemplo mínimo de Gradle Kotlin DSL para um app Android moderno seria:

```kotlin
plugins {
    id("com.android.application")
    kotlin("android")
}

android {
    namespace = "br.dev.kotlin.edge"
    compileSdk = 36

    defaultConfig {
        applicationId = "br.dev.kotlin.edge"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }

    buildFeatures {
        compose = true
    }
}
```

Não faça essa mudança como um commit isolado no fim da sprint. Atualizar `targetSdk` deve vir acompanhado de uma bateria de telas críticas, porque a regressão visual aparece em tempo de execução, não necessariamente no build.

## Ative edge-to-edge na Activity

Em apps Compose, o ponto de entrada normalmente é a `ComponentActivity`. A recomendação moderna é ativar edge-to-edge cedo, antes de desenhar o conteúdo:

```kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)

        setContent {
            KotlinBrasilTheme {
                AppRoot()
            }
        }
    }
}
```

Esse passo sozinho não resolve layout. Ele apenas coloca o app no modo certo. A partir daqui, cada tela precisa decidir quais áreas podem ser decorativas e quais áreas precisam de padding para não ficarem sob as barras do sistema.

## Use Scaffold com insets explícitos

Um padrão seguro é deixar o `Scaffold` receber os insets corretos e propagar o padding para o conteúdo:

```kotlin
@Composable
fun FeedScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Artigos Kotlin") }
            )
        },
        contentWindowInsets = WindowInsets.safeDrawing
    ) { innerPadding ->
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = innerPadding
        ) {
            items(artigos) { artigo ->
                ArtigoCard(artigo)
            }
        }
    }
}
```

`WindowInsets.safeDrawing` é um bom ponto de partida para telas em que todo conteúdo importante precisa ficar visível. Ele considera áreas onde desenho pode ser inseguro por causa de barras do sistema e cutouts.

Mas nem toda tela deve usar o mesmo comportamento. Uma tela com imagem de capa pode desenhar a imagem atrás da status bar e aplicar padding apenas ao título ou aos botões:

```kotlin
@Composable
fun DetalheArtigoScreen() {
    Box(Modifier.fillMaxSize()) {
        HeaderImage(
            modifier = Modifier
                .fillMaxWidth()
                .height(260.dp)
        )

        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(WindowInsets.safeDrawing.asPaddingValues())
        ) {
            BackButton()
            Spacer(Modifier.height(180.dp))
            ArticleBody()
        }
    }
}
```

A diferença é intencional: decoração pode ocupar a borda; ação e texto precisam respeitar área segura.

## Não some padding duas vezes

Um bug clássico em Compose é aplicar insets no `Scaffold`, depois aplicar `statusBarsPadding()` ou `navigationBarsPadding()` dentro do conteúdo e acabar com espaçamento duplicado.

Evite misturar padrões sem necessidade:

```kotlin
// Suspeito: pode duplicar padding dependendo da tela
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
    Column(
        Modifier
            .padding(innerPadding)
            .statusBarsPadding()
    ) {
        Conteudo()
    }
}
```

Prefira um ponto de verdade por tela. Se o `Scaffold` controla o padding, passe `innerPadding` para a área rolável. Se a tela é customizada com `Box`, use `WindowInsets.safeDrawing.asPaddingValues()` manualmente. O time deve conseguir olhar a tela e responder: “quem é responsável pelos insets aqui?”.

## Listas precisam de rodapé seguro

Em apps reais, a maioria das regressões aparece em listas. O último item fica atrás da navigation bar, um botão flutuante cobre conteúdo ou o scroll não permite enxergar a ação final.

Uma `LazyColumn` com CTA fixo no rodapé precisa combinar insets com espaçamento extra:

```kotlin
@Composable
fun VagasKotlinScreen() {
    Box(Modifier.fillMaxSize()) {
        LazyColumn(
            modifier = Modifier.fillMaxSize(),
            contentPadding = PaddingValues(
                start = 16.dp,
                top = 16.dp,
                end = 16.dp,
                bottom = 96.dp
            )
        ) {
            items(vagas) { vaga ->
                VagaCard(vaga)
            }
        }

        Button(
            onClick = { /* salvar alerta */ },
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .navigationBarsPadding()
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Criar alerta de vaga")
        }
    }
}
```

Aqui, a lista ganha espaço para não terminar sob o botão, e o botão respeita a navigation bar. Em telas com FAB, a lógica é a mesma: o componente fixo deve ter padding de sistema, e a área rolável precisa de bottom padding suficiente para não ficar escondida.

## Teclado: imePadding não é opcional

Formulários são outra fonte de dor. Edge-to-edge com teclado aberto exige tratar IME inset. Uma tela de login simples pode usar:

```kotlin
@Composable
fun LoginScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .safeDrawingPadding()
            .imePadding()
            .verticalScroll(rememberScrollState())
            .padding(24.dp),
        verticalArrangement = Arrangement.Center
    ) {
        Text("Entrar", style = MaterialTheme.typography.headlineMedium)
        Spacer(Modifier.height(24.dp))
        OutlinedTextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("E-mail") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(Modifier.height(12.dp))
        OutlinedTextField(
            value = senha,
            onValueChange = { senha = it },
            label = { Text("Senha") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(Modifier.height(24.dp))
        Button(onClick = ::entrar, modifier = Modifier.fillMaxWidth()) {
            Text("Continuar")
        }
    }
}
```

`imePadding()` desloca o conteúdo quando o teclado aparece. O `verticalScroll` garante que telas menores ainda consigam acessar o botão. Sem isso, a tela pode parecer correta em um Pixel grande e quebrar em aparelhos intermediários, tablets dobráveis ou layouts com fonte aumentada.

## Top bars e status bar transparente

Com edge-to-edge, barras superiores devem parecer parte da UI, não uma faixa quebrada. Em Compose Material 3, `TopAppBar` funciona bem, mas você precisa decidir se ela terá cor sólida, translúcida ou se ficará sobre uma imagem.

Para telas comuns:

```kotlin
TopAppBar(
    title = { Text("Configurações") },
    colors = TopAppBarDefaults.topAppBarColors(
        containerColor = MaterialTheme.colorScheme.surface
    )
)
```

Para hero visual, use contraste forte e teste legibilidade em modo claro, escuro e com imagem carregando lentamente. O problema não é só técnico; é de UX. Um ícone branco sobre imagem clara atrás da status bar vira bug de acessibilidade.

## Cutouts e telas grandes

Edge-to-edge também conversa com tablets, dobráveis e display cutouts. Não presuma que status bar fica sempre no topo ou que navigation bar fica sempre embaixo. Em landscape, foldables e janelas redimensionáveis, os insets podem mudar bastante.

Por isso, evite constantes mágicas:

```kotlin
// Frágil
Modifier.padding(top = 24.dp, bottom = 48.dp)
```

Prefira APIs de inset:

```kotlin
Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
```

E combine com layout adaptativo quando a tela crescer. Se o projeto usa duas colunas, navigation rail ou painel mestre-detalhe, revise também nossos conteúdos sobre [Compose Multiplatform](/blog/kotlin-compose-multiplatform-ui-2026/) e [Kotlin Multiplatform](/blog/kotlin-multiplatform/), porque a disciplina de layout responsivo é parecida: estado claro, áreas seguras e componentes que não dependem de um único tamanho de tela.

## Como testar sem depender do “olhômetro”

A migração para edge-to-edge precisa de checklist. Um fluxo básico:

1. rode o app em Android 15 e Android 16;
2. teste navegação por gestos e por três botões;
3. abra teclado em todos os formulários;
4. aumente fonte e display size;
5. teste landscape em telas críticas;
6. capture screenshots antes/depois;
7. rode Macrobenchmark ou testes de screenshot onde fizer sentido.

Para Compose, testes automatizados podem validar se elementos existem e continuam clicáveis, mas screenshot tests ajudam a detectar corte visual. O artigo sobre [Baseline Profiles e Macrobenchmark](/blog/baseline-profiles-macrobenchmark-android-kotlin-2026/) cobre a parte de medição de performance; para edge-to-edge, o foco é combinar essa disciplina com regressão visual e QA manual em aparelhos representativos.

Também vale revisar a pipeline. Se o time usa GitHub Actions, o guia de [CI/CD para Kotlin](/blog/kotlin-devops-ci-cd/) ajuda a organizar checks por pull request. Para comparar com estratégias de validação em outras stacks mobile/backend, há paralelos úteis em <a href="https://python.dev.br/blog" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">automação e testes no ecossistema Python</a>, especialmente na disciplina de separar teste rápido, teste visual e teste end-to-end.

## Checklist de migração

Antes de subir `targetSdk = 36` para produção, revise:

- `enableEdgeToEdge()` chamado na Activity principal;
- nenhum uso cego de padding fixo para status/navigation bar;
- `Scaffold` com `contentWindowInsets` definido conscientemente;
- listas com bottom padding suficiente;
- CTAs fixos com `navigationBarsPadding()`;
- formulários com `imePadding()` e scroll;
- telas com hero image testadas com contraste real;
- bottom sheets e dialogs revisados;
- screenshots em Android 15/16;
- QA com fonte aumentada;
- regressão de performance em telas críticas.

## Erros comuns

O primeiro erro é achar que edge-to-edge é só “deixar transparente”. Transparência sem insets vira conteúdo ilegível.

O segundo é corrigir tela por tela com números mágicos. Isso funciona até trocar aparelho, orientação ou modo de navegação.

O terceiro é ignorar teclado. Muitas migrações parecem boas até o usuário editar um campo no fim da tela.

O quarto é duplicar padding. Se a tela parece “baixa demais”, desconfie de `Scaffold` mais `safeDrawingPadding()` no mesmo eixo.

O quinto é não envolver design e QA. Edge-to-edge muda densidade visual, hierarquia e contraste. Não é apenas uma tarefa de Android.

## Conclusão

Android 16 transforma edge-to-edge em requisito prático para apps Kotlin modernos. A migração não precisa ser traumática, mas precisa ser deliberada: ative o modo corretamente, escolha o responsável pelos insets em cada tela, trate listas e formulários como casos críticos e valide em dispositivos reais.

Para times que já usam Compose, coroutines, Navigation 3 e CI/CD, essa é uma boa oportunidade de elevar a qualidade visual do app. Em vez de “consertar barra do sistema”, pense em uma UI que entende a janela inteira, protege conteúdo importante e aproveita melhor o espaço disponível.

O resultado é um app mais atual, mais consistente e pronto para a próxima onda de aparelhos Android — de celulares compactos a dobráveis, tablets e janelas redimensionáveis.
