Configura inserciones de ventana

Para permitir que tu app tenga control total sobre dónde dibuja contenido, sigue estos pasos de configuración. Si no sigues estos pasos, es posible que tu app dibuje colores negros o sólidos detrás de la IU del sistema, o que no se anime de forma síncrona con el teclado de software.

  1. Establece como objetivo Android 15 (nivel de API 35) o versiones posteriores para aplicar de borde a borde en Android 15 y versiones posteriores. Tu app se muestra detrás de la IU del sistema. Puedes ajustar la IU de tu app controlando las inserciones.
  2. Opcionalmente, llama a enableEdgeToEdge() en Activity.onCreate(), lo que permite que tu app sea de borde a borde en versiones anteriores de Android.
  3. Establece android:windowSoftInputMode="adjustResize" en la entrada AndroidManifest.xml de tu actividad. Este parámetro de configuración permite que tu app reciba el tamaño del IME de software como inserciones, lo que te ayuda a aplicar el diseño y el padding adecuados cuando el IME aparece y desaparece en tu 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">
    

Usa las APIs de Compose

Una vez que tu Activity haya tomado el control del procesamiento de todas las inserciones, puedes usar las APIs de Compose para asegurarte de que el contenido no esté oculto y de 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 inserción.

Por ejemplo, este es el método más básico para aplicar las inserciones 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 de todo el contenido de la app. Si bien esto garantiza que los elementos interactivos no se superpongan con la IU del sistema, también significa que ninguna parte 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 toda la ventana, debes ajustar con precisión dónde se aplican las inserciones en cada pantalla o componente.

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

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

Modificadores de padding

Modifier.windowInsetsPadding(windowInsets: WindowInsets) aplica las inserciones de ventana proporcionadas como padding, y funciona igual que Modifier.padding. Por ejemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing) aplica las inserciones de dibujo seguras como relleno en los 4 lados.

También hay varios métodos de utilidad integrados para los tipos de inserción 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 inserción.

Modificadores de tamaño de inserción

Los siguientes modificadores aplican una cantidad de inserciones de ventana estableciendo 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 lado final 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 especialmente útiles para dimensionar un Spacer que ocupa el espacio de las inserciones:

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

Consumo de inserciones

Los modificadores de padding de inserción (windowInsetsPadding y asistentes como safeDrawingPadding) consumen automáticamente la porción de las inserciones que se aplican como padding. A medida que profundizas en el árbol de composición, los modificadores de padding de inserción anidados y los modificadores de tamaño de inserción saben que los modificadores de padding de inserción externos ya consumieron una parte de las inserciones y evitan usar la misma parte de las inserciones más de una vez, lo que generaría demasiado espacio adicional.

Los modificadores de tamaño de inserción también evitan usar la misma porción 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 observamos el mismo ejemplo de LazyColumn que antes, el modificador imePadding cambia el tamaño de LazyColumn. Dentro de LazyColumn, el último elemento se dimensiona para que tenga 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 del Spacer será el tamaño del lado inferior de las barras del sistema.

Cuando se abre el IME, las inserciones del IME se animan para coincidir 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 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 ocurren en sentido inverso: el Spacer comienza a expandirse desde una altura de cero una vez que el imePadding() aplica menos que el lado inferior de las barras del sistema, hasta que finalmente el 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 de windowInsetsPadding y se puede influir 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 inserción, para indicar a los elementos secundarios que ya se consumió una cierta cantidad de inserciones:

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 se proporciona relleno o espaciado a través de algún otro mecanismo que no sean los modificadores de relleno de inserción, como un Modifier.padding común o espaciadores de altura fija:

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

En los casos en los que se necesiten las inserciones de ventana sin procesar sin consumo, usa los valores de WindowInsets directamente o usa WindowInsets.asPaddingValues() para devolver un PaddingValues de las inserciones que no se ven afectadas por el consumo. Sin embargo, debido a las siguientes advertencias, es preferible usar los modificadores de relleno de inserciones de ventana y los modificadores de tamaño de inserciones de ventana siempre que sea posible.

Inserciones y fases de Jetpack Compose

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

El valor de las inserciones 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 se retrasa un fotograma. Los modificadores integrados que se describen en esta página se compilan para retrasar el uso de los valores de las inserciones hasta la fase de diseño, lo que garantiza que los valores de inserción se usen en el mismo fotograma en el que se actualizan.