Acerca de WindowInsetsRulers

WindowInsets es la API estándar en Jetpack Compose para controlar las áreas de la pantalla que están parcial o totalmente ocultas por la IU del sistema. Estas áreas incluyen la barra de estado, la barra de navegación y el teclado en pantalla. También puedes pasar WindowInsetsRulers predefinidos, como SafeDrawing a Modifier.fitInside o Modifier.fitOutside para alinear tu contenido con las barras del sistema y el corte de pantalla, o bien crear WindowInsetsRulers personalizados.

Ventajas de WindowInsetsRulers

  • Evita la complejidad del consumo: Opera durante la fase de colocación del diseño. Esto significa que omite por completo la cadena de consumo de inserciones y siempre puede proporcionar las posiciones absolutas y correctas de las barras del sistema y los cortes de pantalla, independientemente de lo que hayan hecho los diseños principales. Usar los métodos Modifier.fitInside o Modifier.fitOutside es útil para solucionar problemas cuando los elementos Modifier.fitInside superiores consumen de forma incorrecta las inserciones.
  • Evita fácilmente las barras del sistema: Ayuda al contenido de tu app a evitar las barras del sistema y el corte de pantalla, y puede ser más sencillo que usar WindowInsets directamente.
  • Altamente personalizable: Los desarrolladores pueden alinear el contenido con reglas personalizadas y tener un control preciso sobre sus diseños con diseños personalizados.

Desventajas de WindowInsetsRulers

  • No se puede usar para la medición: Debido a que opera durante la fase de colocación, la información posicional que proporciona no está disponible durante la fase de medición anterior.
  • Potencial de inestabilidad del diseño: Esto puede provocar fallas si el tamaño de un diseño principal depende del tamaño de sus elementos secundarios. Dado que un elemento secundario que usa WindowInsetsRulers podría cambiar su posición o tamaño durante la colocación, puede crear un ciclo de diseño inestable.

Crea WindowInsetsRulers personalizados

Puedes alinear el contenido con reglas personalizadas. Por ejemplo, considera el caso de uso en el que un elemento componible principal controla de forma incorrecta las inserciones, lo que provoca problemas de padding en un elemento secundario de nivel inferior. Si bien este problema se puede resolver de otras maneras, incluso con Modifier.fitInside, también puedes crear una regla personalizada para alinear con precisión el elemento componible secundario sin tener que corregir el problema en el elemento principal upstream, como se muestra en el siguiente ejemplo y video:

@Composable
fun WindowInsetsRulersDemo(modifier: Modifier) {
    Box(
        contentAlignment = BottomCenter,
        modifier = modifier
            .fillMaxSize()
            // The mistake that causes issues downstream, as .padding doesn't consume insets.
            // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars),
            // assume it's difficult to identify this issue to see how WindowInsetsRulers can help.
            .padding(WindowInsets.navigationBars.asPaddingValues())
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier
                // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child
                // Composable without having to fix the parent upstream.
                .alignToSafeDrawing()

            // .imePadding()
            // .fillMaxWidth()
        )
    }
}

fun Modifier.alignToSafeDrawing(): Modifier {
    return layout { measurable, constraints ->
        if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
            val placeable = measurable.measure(constraints)
            val width = placeable.width
            val height = placeable.height
            layout(width, height) {
                val bottom = WindowInsetsRulers.SafeDrawing.current.bottom
                    .current(0f).roundToInt() - height
                val right = WindowInsetsRulers.SafeDrawing.current.right
                    .current(0f).roundToInt()
                val left = WindowInsetsRulers.SafeDrawing.current.left
                    .current(0f).roundToInt()
                measurable.measure(Constraints.fixed(right - left, height))
                    .place(left, bottom)
            }
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

En el siguiente video, se muestra un ejemplo del consumo problemático de la inserción del IME causado por un elemento superior upstream en la imagen de la izquierda y el uso de reglas personalizadas para solucionar el problema en la imagen de la derecha. Se muestra un relleno adicional debajo del elemento TextField Composable, ya que el elemento principal no consumió el relleno de la barra de navegación. El hijo se coloca en la ubicación correcta de la imagen derecha con una regla personalizada, como se ve en la muestra de código anterior.

Verifica que los elementos principales estén restringidos

Para usar WindowInsetsRulers de forma segura, asegúrate de que la madre o el padre proporcione restricciones válidas. Los elementos superiores deben tener un tamaño definido y no pueden depender del tamaño de un elemento secundario que use WindowInsetsRulers. Usa fillMaxSize o algún otro modificador de tamaño en elementos componibles principales.

Del mismo modo, colocar un elemento componible que use WindowInsetsRulers dentro de un contenedor de desplazamiento, como verticalScroll, puede provocar un comportamiento inesperado, ya que el contenedor de desplazamiento proporciona restricciones de altura no delimitadas, que son incompatibles con la lógica de la regla.