wandres.dev
UI ADAPTATIVA · plegables, tablets y XR

Layouts canónicos y scaffolds adaptativos

Frente a la tentación de inventar una disposición distinta por pantalla, la plataforma propone un catálogo cerrado de tres patrones que cubren la inmensa mayoría de los casos reales: lista y detalle, panel de apoyo y feed. Esta lección explica qué problema resuelve cada uno y cuándo aplica, describe el mecanismo de los scaffolds de tres paneles con su navegador, sus directivas y sus estrategias de adaptación, sitúa `NavigationSuiteScaffold` como la pieza que mueve la navegación al borde correcto según el espacio, y cierra con las trampas de estado que convierten un patrón correcto en una experiencia rota.

⏱ 21 min

Una vez medida la ventana, queda la pregunta que de verdad importa: qué se hace con el espacio. La respuesta ingenua es inventar; la respuesta madura es reconocer que este problema ya se resolvió, que se resolvió pocas veces y bien, y que el catálogo resultante cabe en tres patrones. Lista y detalle, para relaciones de uno a muchos donde el usuario navega entre elementos comparables. Panel de apoyo, para contenido principal acompañado de material secundario que gana al estar visible a la vez. Y feed, para colecciones extensas y heterogéneas sin jerarquía dominante. Alrededor de esos tres patrones la biblioteca adaptativa construye unas estructuras que no son contenedores decorativos sino máquinas de estados completas: saben cuántos paneles caben, cuál mostrar cuando no caben todos, cómo tratar la bisagra de un plegable, qué hacer al retroceder y cómo animar la transición. Entender esas máquinas es la diferencia entre usarlas y pelearse con ellas.

🎯 Al terminar esta lección sabrás
  • Elegir con criterio entre lista-detalle, panel de apoyo y feed a partir de la naturaleza del contenido.
  • Explicar el papel del navegador de tres paneles, de la directiva y de las estrategias de adaptación.
  • Colocar la navegación en el borde correcto según el espacio mediante NavigationSuiteScaffold.
  • Anticipar los defectos de estado y de retroceso que aparecen al cambiar de disposición.

Los tres patrones y qué problema resuelve cada uno

Lista y detalle es el patrón de la relación uno a muchos. Su tesis es que, cuando el usuario recorre una colección comparando elementos, obligarle a ir y volver entre dos pantallas destruye el contexto y multiplica la interacción. En compacto se comporta como siempre, con dos destinos y una navegación entre ellos; en cuanto hay espacio, ambos coexisten y la selección deja de ser un salto para convertirse en un cambio de contenido del panel derecho. Aplica al correo, a los ajustes, a los mensajes, a los catálogos y a cualquier bandeja.

Panel de apoyo es el patrón de la información complementaria. Su tesis es distinta y a menudo se confunde con la anterior: aquí no hay dos entidades comparables sino una principal y un material que la enriquece —comentarios, propiedades, historial, herramientas, filtros—. El panel de apoyo no es un destino de navegación y su contenido no sustituye al principal sino que lo acompaña. Cuando el espacio falta, no compite por él: se pliega a una hoja, a una pestaña o a un botón.

Feed es el patrón de la colección extensa sin jerarquía. Su tesis es que, cuando no hay un elemento privilegiado, la mejor respuesta al espacio adicional es aumentar la densidad de la retícula, no crear paneles. La decisión clave aquí no es cuántos paneles sino cuántas columnas, y se toma midiendo el hueco disponible con un tamaño mínimo por celda en lugar de fijando un número.

💡
El criterio que separa lista-detalle de panel de apoyo

Pregúntate si el contenido del segundo panel tiene sentido por sí solo como destino. El correo abierto sí lo tiene: es una entidad con identidad propia, se puede enlazar y compartir. El panel de comentarios no: solo existe respecto a algo. Si tiene identidad propia, es lista-detalle y necesita navegación y retroceso; si no la tiene, es apoyo y necesita visibilidad conmutable.

La máquina de estados de los scaffolds de tres paneles

Las estructuras adaptativas de la biblioteca organizan hasta tres paneles con papeles definidos —principal, secundario y terciario— y gobiernan su visibilidad mediante tres piezas que conviene distinguir con precisión, porque la mayoría de las dificultades vienen de confundirlas.

La primera es la directiva, que responde a cuántos paneles caben y con qué separaciones. Se calcula a partir de la información adaptativa de la ventana e incluye, además del tamaño, la presencia y posición de una bisagra. Es el puente entre la lección anterior y esta: la clase de tamaño entra por aquí.

La segunda es el valor de los paneles, que responde a cuál se muestra ahora mismo. No es un simple booleano por panel sino un estado con tres posibilidades —expandido, oculto y reservado— cuya combinación depende de la directiva y del destino actual.

La tercera es el navegador, que es el objeto que se recuerda en la composición y que traduce las intenciones del usuario en transiciones de ese estado. Es también el que integra el retroceso, y ese punto merece énfasis: en una ventana compacta, retroceder desde el detalle debe volver a la lista; en una expandida donde ambos se ven, ese mismo retroceso debe salir de la pantalla. Esa lógica, que escrita a mano genera defectos interminables, vive dentro del navegador.

@Composable
fun PantallaCorreo() {
    val navegador = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navegador.scaffoldDirective,
        value = navegador.scaffoldValue,
        listPane = {
            AnimatedPane {
                ListaDeCorreos(
                    onSeleccionar = { id -> navegador.navigateTo(ListDetailPaneScaffoldRole.Detail, id) },
                )
            }
        },
        detailPane = {
            AnimatedPane {
                val id = navegador.currentDestination?.contentKey
                if (id != null) DetalleDeCorreo(id) else EstadoVacio()
            }
        },
    )
}

Hay dos detalles de ese fragmento que no son adorno. El primero es que el identificador seleccionado viaja dentro del navegador y no en un estado paralelo; mantenerlo aparte es la causa número uno de desincronización entre lo que el navegador cree y lo que la interfaz muestra. El segundo es el estado vacío del panel de detalle: en compacto ese panel nunca se ve sin selección, pero en expandido es lo primero que aparece al abrir la pantalla, y dejarlo en blanco es un defecto visible desde el primer segundo.

flowchart TD
A[Informacion adaptativa de la ventana] --> B[Directiva del scaffold]
B --> C[Cuantos paneles caben]
D[Navegador de tres paneles] --> E[Valor de los paneles]
C --> E
E --> F[Panel expandido]
E --> G[Panel oculto]
E --> H[Panel reservado]
D --> I[Gestion del retroceso]
I --> J[En compacto vuelve al panel anterior]
I --> K[En expandido sale de la pantalla]
style E fill:#a6e3a1,color:#11111b

Cuando un panel no cabe, la estructura aplica una estrategia de adaptación que se declara por panel. Ocultarlo es el comportamiento por defecto y el más conservador. Flotarlo por encima del contenido principal conserva el acceso sin robar sitio y encaja bien con paneles de apoyo. Reorganizarlo hacia otra posición sirve cuando el contenido puede fluir debajo en lugar de al lado. La elección no es indiferente: un panel de apoyo oculto desaparece del alcance del usuario salvo que se le ofrezca una vía explícita de vuelta, mientras que uno flotado la conserva por construcción.

Queda una pieza que aporta mucho por poco esfuerzo: el estado de expansión que permite arrastrar el separador entre paneles. Los usuarios de pantallas grandes esperan poder repartir el espacio a su gusto, la biblioteca ofrece el tirador y el estado, y lo único que hay que aportar es la decisión de conservar la proporción elegida entre sesiones.

La navegación también se mueve

El segundo eje de la adaptación estructural es dónde vive la navegación principal, y responde a una restricción física antes que estética: en un teléfono el pulgar alcanza el borde inferior y no el superior; en una tablet sujeta con dos manos el borde inferior queda lejos del centro visual; en un escritorio con ratón la distancia deja de importar y manda la convención.

De ahí sale la progresión canónica: barra inferior en compacto, raíl lateral en medio, y cajón permanente o raíl expandido en expandido. Escribir esa progresión a mano exige triplicar la declaración de destinos y mantener tres árboles sincronizados. NavigationSuiteScaffold la resuelve declarando los destinos una sola vez y dejando que la pieza elija la forma según la información adaptativa de la ventana.

NavigationSuiteScaffold(
    navigationSuiteItems = {
        destinos.forEach { destino ->
            item(
                selected = destino == destinoActual,
                onClick = { irA(destino) },
                icon = { Icon(destino.icono, contentDescription = null) },
                label = { Text(destino.etiqueta) },
            )
        }
    },
) {
    ContenidoDeLaPantalla()
}

La forma elegida puede forzarse cuando el producto lo exige, y hay dos casos legítimos para hacerlo. Uno es el de las aplicaciones con muchísimos destinos, donde el raíl se queda corto y conviene saltar antes al cajón. Otro es el del contenido inmersivo a pantalla completa, donde interesa retirar la navegación del borde y no simplemente reubicarla. Fuera de esos casos, forzar la forma es reintroducir a mano una decisión que la pieza ya toma bien.

⚠️
La navegacion cambia de sitio pero no de contenido

El error más frecuente al adoptar esta pieza consiste en aprovechar el cambio de forma para cambiar también el conjunto de destinos: cinco en la barra inferior, ocho en el cajón porque caben. El resultado es que tres destinos existen o no según el ancho de la ventana, algo que el usuario percibe como pérdida de funcionalidad al plegar. La forma se adapta al espacio; el mapa de la aplicación, no.

Las trampas de estado al cambiar de disposición

Los patrones canónicos funcionan; lo que falla casi siempre es la transición entre ellos. Conviene enumerar los cuatro defectos que más aparecen en revisión.

🔙

Retroceso incoherente

Un retroceso escrito a mano que no consulta la disposición actual cierra la pantalla cuando debía volver a la lista, o al revés. Debe delegarse siempre en el navegador.

🎯

Selección perdida

Al pasar de compacto a expandido, el elemento que el usuario tenía abierto debe quedar seleccionado en el panel de detalle. Si el identificador no sobrevive al cambio de configuración, aparece el estado vacío.

📜

Desplazamiento reiniciado

Cada panel tiene su propio estado de desplazamiento y debe conservarlo por separado a través del cambio. Compartir un único estado entre disposiciones los mezcla.

⌨️

Foco descolocado

Cuando aparece un segundo panel, el foco y el orden de recorrido cambian. Sin intervención, el teclado salta al principio y el lector de pantalla anuncia la pantalla entera de nuevo.

Los cuatro comparten diagnóstico: son consecuencias de que un cambio de geometría es un cambio de configuración, y todo lo que no viva en un contenedor que sobreviva a la recreación se pierde. La regla práctica es que el estado de navegación y de selección pertenece al modelo de vista o a un contenedor equivalente, mientras que el estado puramente visual de cada panel se conserva con los mecanismos de guardado de la composición.

Hay un quinto defecto más sutil que merece mención aparte porque no se manifiesta como pérdida sino como duplicación: la doble fuente de verdad. Ocurre cuando el elemento seleccionado vive a la vez en el navegador y en el modelo de vista, y ambas copias se actualizan por caminos distintos. Durante un tiempo coinciden y todo parece correcto; en cuanto el usuario retrocede desde una entrada profunda o el sistema recrea la actividad, divergen y la interfaz muestra un detalle que no corresponde a la fila resaltada. La corrección no es sincronizarlas mejor, sino elegir una y derivar la otra.

Un catalogo cerrado de patrones es una restriccion que compra composicion

Conviene preguntarse por qué la plataforma propone exactamente tres patrones y no una biblioteca de veinte contenedores flexibles con los que construir cualquier cosa, porque la respuesta ilumina un principio de diseño de sistemas que va mucho más allá de Android. Un catálogo cerrado y pequeño parece, a primera vista, una limitación de la expresividad: hay disposiciones imaginables que no encajan en ninguno de los tres. Y sin embargo, cada patrón del catálogo trae consigo algo que una disposición inventada no puede tener por definición: un comportamiento acordado en todas las dimensiones difíciles. Cuando eliges lista y detalle, no estás eligiendo una geometría de dos columnas; estás heredando una semántica de retroceso que ya distingue entre volver al panel anterior y salir de la pantalla, un orden de recorrido para el lector de pantalla que ya fue pensado, una animación de transición entre disposiciones que ya está calibrada, una respuesta a la bisagra de un plegable que ya está resuelta, y un vocabulario que cualquier persona que se incorpore al equipo reconocerá sin necesidad de leer el código. Todo eso es trabajo que existe con independencia de si lo haces tú o lo hereda tu componente, y su volumen es de un orden de magnitud superior al de dibujar dos columnas. El principio general es que en los sistemas de interfaz la restricción de vocabulario no reduce el poder expresivo sino que lo traslada de nivel: al fijar los pocos patrones estructurales, la creatividad se desplaza a la composición —qué patrón dentro de qué otro, con qué contenido, con qué densidad— que es exactamente donde aporta valor al usuario, y se retira del sitio donde solo aportaba variación gratuita e incompatibilidades. Es la misma lógica por la que la arquitectura de edificios trabaja con un repertorio corto de tipologías de planta y sin embargo ningún edificio se parece a otro. La consecuencia práctica para el trabajo diario es un cambio en el orden de las preguntas. Ante una pantalla nueva, la primera pregunta no debe ser cómo la dibujo en ancho, sino cuál de los tres patrones describe la relación entre estas piezas de contenido. Si la respuesta es ninguno, casi siempre significa que la relación entre esas piezas no está clara todavía, y ese es un problema de modelo de información que ninguna cantidad de código de layout va a resolver.

⚔️ Llevar una pantalla real al catalogo
  1. Elige la pantalla más importante de tu aplicación y clasifícala en uno de los tres patrones. Justifica la elección con el criterio de identidad propia del segundo panel.
  2. Implementa la versión con el scaffold correspondiente y su navegador, guardando el identificador seleccionado dentro del navegador y no aparte.
  3. Diseña y programa el estado vacío del panel de detalle, que en pantalla ancha es lo primero que ve el usuario.
  4. Sustituye tu navegación por NavigationSuiteScaffold declarando los destinos una sola vez y verifica que el conjunto es idéntico en las tres formas.
  5. Recorre los cuatro defectos de estado de la última sección en tu implementación: gira, pliega, retrocede y comprueba selección, desplazamiento y foco en cada transición.