Cómo hacer que tu app funcione en dispositivos plegables

Las pantallas grandes desplegadas y los estados plegados únicos permiten crear nuevas experiencias del usuario en dispositivos plegables. Para que tu app funcione en dispositivos plegables en Jetpack Compose, usa la biblioteca Compose Material 3 Adaptive, que proporciona APIs para las funciones de ventanas de dispositivos plegables, como los pliegues, las bisagras y las posiciones del dispositivo. Cuando tu app funciona en dispositivos plegables, puede adaptar su diseño para evitar colocar contenido importante en el área de pliegues o bisagras y usar esos elementos como separadores naturales.

Comprender si un dispositivo admite configuraciones como la posición de mesa o de libro puede guiar las decisiones sobre la compatibilidad con diferentes diseños o la provisión de funciones específicas.

Información de la ventana

En Jetpack Compose, usa la collectFoldingFeaturesAsState() función de componibilidad para observar la información de plegado de la ventana. collectFoldingFeaturesAsState() recopila las funciones de plegado de la ventana actual de WindowInfoTracker en un State de Compose que contiene una lista de FoldingFeature objetos.

Debido a que collectFoldingFeaturesAsState() muestra un State de Compose, tus elementos componibles se recomponen automáticamente cada vez que cambia el estado o la posición de plegado del dispositivo, sin requerir la administración manual del ciclo de vida ni la recopilación de flujo:

@Composable
fun FoldAwareScreen() {
    val foldingFeatures by collectFoldingFeaturesAsState()

    // Access folding features for the current window.
    val foldFeature = foldingFeatures.firstOrNull()
    if (foldFeature != null) {
        // Adapt layout based on the fold feature.
    }
}

Funciones de las pantallas plegables

La lista de elementos FoldingFeature que muestra collectFoldingFeaturesAsState() (un subtipo de DisplayFeature) proporciona información sobre pantallas plegables, incluidas las siguientes propiedades:

  • state: Es el estado plegado del dispositivo, FLAT o HALF_OPENED.

  • orientation: Es la orientación del pliegue o la bisagra, HORIZONTAL o VERTICAL.

  • occlusionType: Indica si el pliegue o la bisagra oculta parte de la pantalla, NONE o FULL

  • isSeparating: Indica si el pliegue o la bisagra crea dos áreas lógicas de visualización, verdadero o falso.

Un dispositivo plegable que tenga el estado HALF_OPENED siempre informa isSeparating como verdadero porque la pantalla está separada en dos áreas de visualización. Además, isSeparating siempre es verdadero en un dispositivo con pantalla doble cuando la aplicación abarca ambas pantallas.

La propiedad FoldingFeature bounds (heredada de DisplayFeature) representa el rectángulo delimitador de un componente de plegado, como un pliegue o una bisagra. Se pueden usar límites para posicionar elementos en la pantalla en relación con ese componente:

@Composable
fun FoldFeatureDemo() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    if (foldFeature != null) {
        val isSeparating = foldFeature.isSeparating
        val occlusionType = foldFeature.occlusionType
        val bounds = foldFeature.bounds
        // Use bounds and properties to position content.
    }
}

Posición de mesa

Con la información incluida en el objeto FoldingFeature, tu app puede admitir posiciones como la de mesa, en la que el teléfono se ubica sobre una superficie, la bisagra está en posición horizontal y la pantalla plegable está semiabierta.

La posición de mesa ofrece a los usuarios la comodidad de operar el teléfono sin tenerlo en la mano. La postura de mesa es ideal para mirar contenido multimedia, tomar fotos y realizar videollamadas.

Una app de reproductor de video en posición de mesa con reproducción de video en la parte vertical de la pantalla y controles de reproducción en la parte horizontal inferior.
Figura 1. Una app de reproductor de video en posición de mesa: video en la parte vertical de la pantalla y controles de reproducción en la parte horizontal.

Usa FoldingFeature.State y FoldingFeature.Orientation de collectFoldingFeaturesAsState() para determinar si el dispositivo está en posición de mesa

@Composable
fun TabletopContent() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    val isTableTopPosture = foldFeature != null &&
        foldFeature.state == FoldingFeature.State.HALF_OPENED &&
        foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL

    if (isTableTopPosture) {
        // Tabletop layout: separate content and controls across the fold.
    } else {
        // Standard layout.
    }
}

También puedes determinar si la ventana está en posición de mesa verificando la Posture propiedad que muestra currentWindowAdaptiveInfoV2():

val adaptiveInfo = currentWindowAdaptiveInfoV2()
val isTabletop = adaptiveInfo.windowPosture.isTabletop

Una vez que sepas que el dispositivo está en posición de mesa, actualiza el diseño de tu app según corresponda. En el caso de las apps de multimedia, eso suele implicar que se coloca el contenido que se reproduce por encima del pliegue y los controles de posicionamiento, así como el contenido complementario, justo debajo para obtener una experiencia de reproducción sin usar las manos.

Ejemplos

Posición del libro

Otra función plegable única es la posición de libro, en la que el dispositivo está medio abierto y el pliegue es vertical. La posición de libro es ideal para leer libros electrónicos. Con un diseño de dos páginas en una pantalla plegable grande y abierta como un libro, la posición de libro captura la experiencia de leer un libro real.

También se puede utilizar para fotografías si quieres capturar una relación de aspecto diferente mientras tomas fotos con el modo manos libres.

Implementa la posición de libro con las mismas técnicas que se usan para la posición de mesa. Verifica que la orientación del componente de plegado sea vertical en lugar de horizontal:

@Composable
fun BookContent() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    val isBookPosture = foldFeature != null &&
        foldFeature.state == FoldingFeature.State.HALF_OPENED &&
        foldFeature.orientation == FoldingFeature.Orientation.VERTICAL

    if (isBookPosture) {
        // Book posture layout: two-page layout across the vertical fold.
    } else {
        // Standard single-page layout.
    }
}

Cambios en el tamaño de la ventana

El área de visualización de una app puede cambiar como resultado de un cambio en la configuración del dispositivo. Por ejemplo, cuando se pliega o se despliega el dispositivo, se rota o se cambia el tamaño en el modo multiventana.

Para recuperar la clase de tamaño de ventana actual y adaptarte a los cambios de tamaño en Jetpack Compose, usa la currentWindowAdaptiveInfoV2() función de componibilidad de la biblioteca Compose Material 3 Adaptive. currentWindowAdaptiveInfoV2() muestra una instancia de WindowAdaptiveInfo que proporciona la WindowSizeClass de la ventana actual (con compatibilidad para puntos de interrupción grandes y extragrandes de forma predeterminada), así como la windowPosture del dispositivo:

@Composable
fun AdaptiveApp() {
    val adaptiveInfo = currentWindowAdaptiveInfoV2()
    val windowSizeClass = adaptiveInfo.windowSizeClass

    when {
        windowSizeClass.isWidthAtLeastBreakpoint(
            WIDTH_DP_EXPANDED_LOWER_BOUND
        ) -> {
            // Expanded layout
        }
        windowSizeClass.isWidthAtLeastBreakpoint(
            WIDTH_DP_MEDIUM_LOWER_BOUND
        ) -> {
            // Medium layout
        }
        else -> {
            // Compact layout
        }
    }
}

Consulta Cómo usar clases de tamaño de ventana.

Recursos adicionales

Para obtener más información sobre los diseños aptos para dispositivos plegables y los dispositivos plegables, consulta los siguientes recursos:

Ejemplos

  • Ejemplo de Jetpack WindowManager: Ejemplo de uso de la biblioteca de Jetpack WindowManager
  • Jetcaster : Implementación de posición de mesa con Compose

Codelabs