Asegúrate de que tu IU funcione con inserciones de ventanas

Una vez que tu actividad haya tomado el control de la administración de todos los insertos, puedes usar las APIs de Compose para asegurarte de que el contenido no se oculte y que los elementos interactivos no se superpongan con la IU del sistema. Estas APIs también sincronizan el diseño de tu app con los cambios de insert.

Por ejemplo, este es el método más básico para aplicar los insertos al contenido de toda tu app:

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

    enableEdgeToEdge()

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

Este fragmento aplica las inserciones de ventana safeDrawing como relleno alrededor del contenido completo de la app. Si bien esto garantiza que los elementos interactivos no se superpongan con la IU del sistema, también significa que nada de la app se dibujará detrás de la IU del sistema para lograr un efecto de borde a borde. Para aprovechar al máximo la ventana, debes ajustar dónde se aplican las inserciones en cada pantalla o componente.

Todos estos tipos de inserciones se animan automáticamente con animaciones del IME que se retroportaron a la API 21. Por extensión, todos tus diseños que usan estos rellenos también se animan automáticamente a medida que cambian los valores de relleno.

Existen dos formas principales de usar estos tipos de inserciones para ajustar tus diseños componibles: modificadores de padding y modificadores de tamaño de inserción.

Modificadores de padding

Modifier.windowInsetsPadding(windowInsets: WindowInsets) aplica las inserciones de ventana determinadas como padding, actuando de la misma manera que lo haría Modifier.padding. Por ejemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing) aplica los inserciones de dibujo seguras como relleno en los 4 lados.

También hay varios métodos de utilidad integrados para los tipos de inserciones más comunes. Modifier.safeDrawingPadding() es uno de esos métodos, equivalente a Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Existen modificadores análogos para los otros tipos de inserciones.

Modificadores de tamaño de la inserción

Los siguientes modificadores aplican una cantidad de inserciones de ventana configurando el tamaño del componente para que sea el tamaño de las inserciones:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Aplica el lado inicial de windowInsets como el ancho (como Modifier.width).

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Aplica el extremo de windowInsets como el ancho (como Modifier.width).

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Aplica el lado superior de windowInsets como la altura (como Modifier.height).

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Aplica el lado inferior de windowInsets como la altura (como Modifier.height).

Estos modificadores son particularmente útiles para ajustar el tamaño de un Spacer que ocupa el espacio de los elementos intercalados:

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

Consumo de intercalación

Los modificadores de padding de inset (windowInsetsPadding y ayudantes como safeDrawingPadding) consumen automáticamente la parte de los insets que se aplican como padding. Mientras se profundiza en el árbol de composición, los modificadores de padding de inset anidados y los modificadores de tamaño de inset saben que algunos de los insets ya fueron consumidos por los modificadores de padding de inset externo y evitan usar la misma parte de los insets más de una vez, lo que generaría demasiado espacio adicional.

Los modificadores de tamaño de inserciones también evitan usar la misma parte de inserciones más de una vez si ya se consumieron. Sin embargo, como cambian su tamaño directamente, no consumen inserciones.

Como resultado, los modificadores de padding anidados cambian automáticamente la cantidad de padding aplicado a cada elemento componible.

Si observas el mismo ejemplo de LazyColumn que antes, el modificador imePadding cambia el tamaño de LazyColumn. Dentro de LazyColumn, el último elemento tiene el tamaño de la altura de la parte inferior de las barras del sistema:

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

Cuando el IME está cerrado, el modificador imePadding() no aplica padding, ya que el IME no tiene altura. Como el modificador imePadding() no aplica relleno, no se consumen inserciones, y la altura de Spacer será del tamaño del lado inferior de las barras del sistema.

Cuando se abre el IME, los inserciones del IME se animan para que coincidan con el tamaño del IME, y el modificador imePadding() comienza a aplicar padding inferior para cambiar el tamaño de LazyColumn a medida que se abre el IME. A medida que el modificador imePadding() comienza a aplicar el padding inferior, también comienza a consumir esa cantidad de inserciones. Por lo tanto, la altura de Spacer comienza a disminuir, ya que el modificador imePadding() ya aplicó parte del espaciado para las barras del sistema. Una vez que el modificador imePadding() aplica una cantidad de padding inferior que es mayor que las barras del sistema, la altura de Spacer es cero.

Cuando se cierra el IME, los cambios se producen a la inversa: Spacer comienza a expandirse desde una altura de cero una vez que imePadding() aplica menos que el lado inferior de las barras del sistema, hasta que finalmente Spacer coincide con la altura del lado inferior de las barras del sistema una vez que el IME se anima por completo.

Figura 2. Columna diferida de borde a borde con TextField.

Este comportamiento se logra a través de la comunicación entre todos los modificadores windowInsetsPadding y puede verse influenciado de otras maneras.

Modifier.consumeWindowInsets(insets: WindowInsets) también consume inserciones de la misma manera que Modifier.windowInsetsPadding, pero no aplica las inserciones consumidas como padding. Esto es útil en combinación con los modificadores de tamaño de los rellenos para indicar a los elementos hermanos que ya se consumió una cantidad determinada de rellenos:

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 manera muy similar a la versión con un argumento WindowInsets, pero toma un PaddingValues arbitrario para consumir. Esto es útil para informar a los elementos secundarios cuando el padding o el espaciado se proporcionan a través de algún otro mecanismo que no sean los modificadores de padding intercalado, como un Modifier.padding ordinario o espaciadores de altura fija:

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

En los casos en que se necesiten las inserciones de ventana sin procesar sin consumo, usa los valores de WindowInsets directamente o usa WindowInsets.asPaddingValues() para mostrar un PaddingValues de las inserciones que no se ven afectadas por el consumo. Sin embargo, debido a las advertencias que se indican a continuación, prefiere usar los modificadores de padding de inserciones de ventana y los modificadores de tamaño de inserciones de ventana siempre que sea posible.

Insets y fases de Jetpack Compose

Compose usa las APIs principales subyacentes de AndroidX para actualizar y animar los insertos, que usan las APIs de la plataforma subyacentes que administran los insertos. Debido a ese comportamiento de la plataforma, los insertos tienen una relación especial con las fases de Jetpack Compose.

El valor de los rellenos se actualiza después de la fase de composición, pero antes de la fase de diseño. Esto significa que, por lo general, leer el valor de las inserciones en la composición usa un valor de las inserciones que está un fotograma atrasado. Los modificadores integrados que se describen en esta página se compilan para retrasar el uso de los valores de los rellenos hasta la fase de diseño, lo que garantiza que los valores de relleno se usen en el mismo marco a medida que se actualizan.