Garantir que a interface funcione com encartes de janela

Depois que a atividade assumir o controle do processamento de todos os insets, você poderá usar as APIs do Compose para garantir que o conteúdo não seja obscurecido e que os elementos interativos não se sobreponham à interface do sistema. Essas APIs também sincronizam o layout do app com as mudanças de engaste.

Por exemplo, este é o método mais básico de aplicar os insets ao conteúdo de todo o app:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Esse snippet aplica os encartes de janela safeDrawing como padding em todo o conteúdo do app. Embora isso garanta que os elementos interativos não se sobreponham à interface do sistema, também significa que nenhum elemento do app será exibido atrás da interface do sistema para alcançar um efeito de borda a borda. Para aproveitar toda a janela, você precisa ajustar onde os insets são aplicados em cada tela ou componente.

Todos esses tipos de inseto são animados automaticamente com animações de IME reportadas para a API 21. Por extensão, todos os layouts que usam esses insets também são animados automaticamente conforme os valores de inset mudam.

Há duas maneiras principais de usar esses tipos de inseto para ajustar os layouts do elemento combinável: modificadores de padding e modificadores de tamanho de inseto.

Modificadores de padding

Modifier.windowInsetsPadding(windowInsets: WindowInsets) aplica os insets de janela fornecidos como padding, agindo exatamente como Modifier.padding. Por exemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing) aplica os engastes de desenho seguros como padding nos quatro lados.

Há também vários métodos utilitários integrados para os tipos de inset mais comuns. Modifier.safeDrawingPadding() é um desses métodos, equivalente a Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Há modificadores análogos para os outros tipos de inset.

Modificadores de tamanho de inserção

Os modificadores a seguir aplicam uma quantidade de engastes de janela definindo o tamanho do componente como o tamanho dos engastes:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Aplica o lado inicial de windowInsets como a largura (como Modifier.width).

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Aplica o lado final de windowInsets como a largura (como Modifier.width).

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Aplica a parte de cima de windowInsets como a altura (como Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Aplica a parte inferior de windowInsets como a altura (como Modifier.height).

Esses modificadores são especialmente úteis para dimensionar um Spacer que ocupa o espaço de insets:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Consumo de Inset

Os modificadores de padding de inserção (windowInsetsPadding e auxiliares como safeDrawingPadding) consomem automaticamente a parte dos insets que são aplicadas como padding. Ao se aprofundar na árvore de composição, os modificadores de padding anexados e os modificadores de tamanho de anexos aninhados sabem que parte dos anexados já foi consumida por modificadores de padding externo e evitam usar a mesma parte dos anexos mais de uma vez, o que resultaria em muito espaço extra.

Os modificadores de tamanho de inseto também evitam usar a mesma parte de insetos mais de uma vez se os insetos já foram consumidos. No entanto, como elas mudam de tamanho diretamente, elas não consomem insets.

Como resultado, o aninhamento de modificadores de padding muda automaticamente a quantidade de padding aplicada a cada elemento combinável.

Analisando o mesmo exemplo de LazyColumn anterior, o LazyColumn está sendo redimensionado pelo modificador imePadding. Dentro do LazyColumn, o último item é dimensionado para ser a altura da parte de baixo das barras do sistema:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Quando o IME está fechado, o modificador imePadding() não aplica padding, já que o IME não tem altura. Como o modificador imePadding() não está aplicando padding, nenhum inseto está sendo consumido, e a altura da Spacer será o tamanho da parte de baixo das barras do sistema.

Quando o IME é aberto, os insets são animados para corresponder ao tamanho do IME, e o modificador imePadding() começa a aplicar o padding inferior para redimensionar o LazyColumn conforme o IME é aberto. À medida que o modificador imePadding() começa a aplicar o padding inferior, ele também começa a consumir essa quantidade de insets. Portanto, a altura do Spacer começa a diminuir, já que parte do espaçamento das barras do sistema já foi aplicado pelo modificador imePadding(). Quando o modificador imePadding() aplica um valor de padding inferior maior que as barras do sistema, a altura do Spacer é zero.

Quando o IME é fechado, as mudanças acontecem de forma inversa: o Spacer começa a se expandir a partir de uma altura de zero quando o imePadding() está aplicando menos que a parte de baixo das barras do sistema, até que o Spacer corresponda à altura da parte de baixo das barras do sistema quando o IME é completamente animado.

Figura 2. Coluna preguiçosa de borda a borda com TextField.

Esse comportamento é alcançado pela comunicação entre todos os modificadores windowInsetsPadding e pode ser influenciado de algumas outras maneiras.

Modifier.consumeWindowInsets(insets: WindowInsets) também consome encartes da mesma forma que Modifier.windowInsetsPadding, mas não aplica os encartes consumidos como padding. Isso é útil em combinação com os modificadores de tamanho de insetos, para indicar aos irmãos que uma certa quantidade de insetos já foi consumida:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

O Modifier.consumeWindowInsets(paddingValues: PaddingValues) se comporta de maneira muito semelhante à versão com um argumento WindowInsets, mas consome um PaddingValues arbitrário. Isso é útil para informar elementos filhos quando o padding ou o espaçamento é fornecido por algum outro mecanismo que não seja os modificadores de padding de inserção, como um Modifier.padding comum ou espaçadores de altura fixa:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

Nos casos em que os insets de janela brutos são necessários sem consumo, use os valores de WindowInsets diretamente ou use WindowInsets.asPaddingValues() para retornar um PaddingValues dos insetos que não são afetados pelo consumo. No entanto, devido às ressalvas abaixo, prefira usar os modificadores de preenchimento e de tamanho de encartes de janela sempre que possível.

Insets e fases do Jetpack Compose

O Compose usa as APIs principais do AndroidX para atualizar e animar insets, que usam as APIs da plataforma para gerenciar insets. Devido a esse comportamento da plataforma, os insets têm uma relação especial com as fases do Jetpack Compose.

O valor dos insets é atualizado após a fase de composição, mas antes da fase de layout. Isso significa que ler o valor dos encartes na composição geralmente usa um valor dos encartes que está atrasado em um frame. Os modificadores integrados descritos nesta página são criados para atrasar o uso dos valores das informações até a fase de layout, o que garante que os valores sejam usados no mesmo frame em que são atualizados.