Configurar encartes de janela

Para permitir que seu app tenha controle total sobre onde o conteúdo é renderizado, siga estas etapas de configuração. Sem essas etapas, o app pode renderizar cores pretas ou sólidas atrás da interface do sistema ou não animar de forma síncrona com o teclado virtual.

  1. Segmente o Android 15 (nível 35 da API) ou versões mais recentes para aplicar o modo de ponta a ponta no Android 15 e versões mais recentes. O app aparece atrás da interface do sistema. Você pode ajustar a interface do app processando encartes.
  2. Se quiser, chame enableEdgeToEdge() em Activity.onCreate(), o que permite que o app seja de ponta a ponta em versões anteriores do Android.
  3. Defina android:windowSoftInputMode="adjustResize" na entrada AndroidManifest.xml da sua atividade. Essa configuração permite que seu app receba o tamanho do IME de software como encartes, o que ajuda a aplicar o layout e o padding adequados quando o IME aparece e desaparece no app.

    <!-- In your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

Usar APIs do Compose

Depois que sua atividade assumir o controle do processamento de todos os encartes, use as APIs do Compose para garantir que o conteúdo não fique oculto e que os elementos interativos não se sobreponham à interface do sistema. Essas APIs também sincronizam o layout do app com mudanças de encarte.

Por exemplo, este é o método mais básico de aplicar encartes 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. Isso garante que os elementos interativos não se sobreponham à interface do sistema, mas também significa que nada do app será renderizado atrás da interface do sistema para alcançar um efeito de ponta a ponta. Para aproveitar ao máximo toda a janela, é necessário ajustar onde os encartes são aplicados em uma tela por tela ou componente por componente.

Todos esses tipos de encarte são animados automaticamente com animações de IME portadas para a API 21. Por extensão, todos os layouts que usam esses encartes também são animados automaticamente à medida que os valores mudam.

Há duas maneiras principais de usar esses tipos de encartes para ajustar os layouts combináveis: modificadores de padding e de tamanho de encarte.

Modificadores de padding

Modifier.windowInsetsPadding(windowInsets: WindowInsets) aplica os encartes de janela especificados como padding, agindo da mesma forma que Modifier.padding. Por exemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing) aplica os encartes de desenho seguros como padding em todos os quatro lados.

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

Modificadores de tamanho de encarte

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

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 o lado de cima de windowInsets como a altura (como Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

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

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

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

Consumo de encartes

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

Os modificadores de tamanho de encarte também evitam usar a mesma parte dos encartes mais de uma vez se eles já tiverem sido consumidos. No entanto, como eles estão mudando o tamanho diretamente, não consomem encartes.

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 de antes, o LazyColumn está sendo redimensionado pelo modificador imePadding. Dentro do LazyColumn, o último item tem o tamanho da altura da parte de baixo das barras de 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 encarte está sendo consumido, e a altura do Spacer será o tamanho da parte de baixo das barras de sistema.

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

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

Figura 2. Coluna de carregamento lento de ponta a ponta com TextField.

Esse comportamento é realizado por meio da comunicação entre todos os modificadores windowInsetsPadding e pode ser influenciado de algumas outras maneiras.

O Modifier.consumeWindowInsets(insets: WindowInsets) também consome encartes da mesma forma que o Modifier.windowInsetsPadding, mas não aplica os encartes consumidos como padding. Isso é útil em combinação com os modificadores de tamanho de encarte, para indicar aos irmãos que uma determinada quantidade de encartes 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))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) se comporta de maneira muito semelhante à versão com um argumento WindowInsets, mas usa um PaddingValues arbitrário para consumir. Isso é útil para informar às crianças quando o padding ou o espaçamento são fornecidos por algum outro mecanismo que não os modificadores de padding de encarte, 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 encartes de janela brutos são necessários sem consumo, use os valores WindowInsets diretamente ou use WindowInsets.asPaddingValues() para retornar um PaddingValues dos encartes que não são afetados pelo consumo. No entanto, devido às seguintes ressalvas, prefira usar os modificadores de preenchimento de encartes de janela e os modificadores de tamanho de encartes de janela sempre que possível.

Encaixes e fases do Jetpack Compose

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

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