Novedades sobre productos

Novedades de la versión de agosto de 2026 de Jetpack Compose

Lectura de 5 min
Ver el perfil de Nick Butcher
Nick Butcher Product Manager

Hoy, la versión de agosto de 2026 de Jetpack Compose es estable. Esta versión incluye la versión 1.12 en los módulos principales de Compose (consulta la asignación completa de la BOM) y presenta APIs visuales enriquecidas, como Mesh Gradients y compatibilidad con Wide Color Gamut (WCG), funciones de diseño estructurales, como áreas con nombre en Grid, integración perfecta con Credential Manager de Android y mejoras significativas en las pruebas y el rendimiento.

Para actualizar tu proyecto a la versión de hoy, actualiza la versión de Compose BOM2026.08.00:

implementation(platform("androidx.compose:compose-bom:2026.08.00"))

Cambios rotundos

  • AGP y SDK de compilación: Compose 1.12 actualiza compileSdk a la API 37, lo que requiere un AGP mínimo de 9.1.2. Como recordatorio, Compose siempre se orientará al compileSdk más reciente. Obtén más información sobre este cambio aquí.
  • Modifier.onFirstVisible() está obsoleta: Migra a Modifier.onVisibilityChanged(), que proporciona un seguimiento más preciso del umbral de visibilidad.

Gráficos

Mesh Gradients

Compose 1.12 presenta MeshGradientPainter para ayudarte a crear gradientes de color orgánicos de varios puntos.

meshGradient.gif
val rows = 1
val columns = 1

val gradientPainter = remember {
    MeshGradientPainter(rows, columns) {
        // Parameters: row, column, position, color
        setVertex(0, 0, Offset(0f, 0f), Color.Red)     // Top-Left
        setVertex(0, 1, Offset(1f, 0f), Color.Blue)    // Top-Right
        setVertex(1, 0, Offset(0f, 1f), Color.Green)   // Bottom-Left
        setVertex(1, 1, Offset(1f, 1f), Color.Yellow)  // Bottom-Right
    }
}

Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

Para obtener más información y ejemplos, consulta la documentación.

Compatibilidad con HDR y Wide Color Gamut

Las pantallas modernas ofrecen una fidelidad de color extendida y un rango dinámico más alto. En Compose 1.12, se habilitó la compatibilidad completa con la canalización para Wide Color Gamut (P3) y la renderización HDR en los gráficos, la pintura y los sombreadores de Compose. Los colores definidos en espacios de color que no son sRGB (como Display P3) se conservan hasta la renderización de la plataforma sin sujeción de color. Los colores volverán de forma segura a sRGB si usan un espacio de color no compatible (p. ej., CieXyz, CieLab o Oklab), dependen de un espacio de color en una versión de Android no compatible (p. ej., Bt2020Hlg en Android 13 y versiones anteriores) o si la app se ejecuta en Android 9 (API 28) y versiones anteriores.

Otros cambios destacados:

  • LayerOutsets se agregó a GraphicsLayer y Modifier.graphicsLayer, que puedes usar para aumentar los límites visuales de la capa más allá de su tamaño medido. Aplica LayerOutsets para evitar el comportamiento clipToBounds implícito cuando la capa se promueve a un búfer fuera de la pantalla.

Estilos

En Google I/O, compartimos nuestra visión inicial de la API de Styles de Compose, una forma unificada y de alto rendimiento de diseñar componentes. Desde entonces, continuamos compilando la arquitectura subyacente para garantizar una seguridad de tipo estricta y una exactitud predecible, y para admitir la compilación de sistemas de diseño personalizados. 

Para asegurarnos de que esta capa fundamental sea correcta, la API seguirá siendo experimental y puedes esperar cambios rotundos. 

Optimizaciones del tiempo de ejecución

Sobrecarga SideEffect con clave

SideEffect ahora admite argumentos clave, lo que te permite activar efectos secundarios únicos cada vez que cambian claves específicas. Esto puede generar un mejor rendimiento en comparación con el uso de LaunchedEffect o DisposableEffect cuando no necesitas la corrutina ni el bloque de eliminación. SideEffect es hasta un 90% más rápido que LaunchedEffect y alrededor de un 20% más rápido que DisposableEffect. Ten en cuenta que SideEffect ejecuta su efecto antes que DisposableEffect y LaunchedEffect, por lo que debes tener cuidado si migras los efectos existentes a esta API, en especial para LaunchedEffects que dependen de que se envíen para comenzar después de que se complete el fotograma actual. 

@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
    SideEffect(key1 = userId, key2 = screenName) {
        analytics.logScreenView(userId, screenName)
    }
}

Animación

Transiciones interactivas de dos etapas

  • Nuevos elementos componiblesDeferredAnimatedContentDeferredAnimatedVisibility permiten crear transiciones agradables de dos etapas, p.ej., para el seguimiento predictivo del gesto de retroceso.
  • Control manual de la animación: Durante la fase diferida de una transición, las propiedades animadas (como la escala o el desplazamiento) ahora se pueden manipular manualmente en tiempo real (p.ej., seguimiento de un gesto de deslizamiento).
  • Transferencia fluida: Una vez que finaliza la fase diferida, el motor de transición toma el control y realiza una transferencia fluida, incluida la transferencia de velocidad, a la transición automática.
  • Compatibilidad con elementos compartidos: Una nueva permitTransformDuringDeferredTransition marca en SharedContentConfig controla si los elementos compartidos se transforman visualmente junto con sus contenedores superiores durante la fase de transición diferida.
val state = remember { DeferredTransitionState(initialScreen) }
val transition = rememberDeferredTransition(state)

if (predictiveBackInProgress) {
    state.defer(targetScreen)
} else {
    state.animateTo(targetScreen)
}

transition.DeferredAnimatedContent(
    targetState = targetScreen,
    mutableTransformSpec = {
       MutableContentTransform {
           // Manually manipulate properties during the deferred phase
           initialContentTransform { scale = swipeProgress }
       }
    }
) { screen ->
    ScreenContent(screen)
}

A continuación, se incluyen dos demostraciones de casos de uso en los que una animación basada en gestos se transfiere a una animación activada:

colorCards.gif

Integraciones de texto, entrada y plataforma

Formato de texto editable

Las nuevas APIs ofrecen formato de texto enriquecido para texto editable en BasicTextField. Ahora puedes aplicar y manipular de forma programática el formato de caracteres y párrafos intercalados con SpanStyleParagraphStyle a través del nuevo método addStyle() dentro de un alcance TextFieldBuffer (como dentro de textFieldState.edit { ... } o una InputTransformation). Además, TextFieldBuffer proporciona APIs getSpanStyles()getParagraphStyles() que muestran objetos TrackedRange, lo que te permite leer, actualizar o quitar estilos aplicados. Para complementar la creación de formato, TextFieldState ahora expone una propiedad textStyles de solo lectura para consultar estilos activos en rangos, mientras que TextFieldBuffer proporciona originalTextStyles para inspeccionar el estado de formato antes de una edición. El formato de texto y las anotaciones personalizadas se conservan en los cambios de configuración.

val state = rememberTextFieldState("Formatted text in Compose 1.12")

// Apply bold and color styles to a range of text
state.edit {
    addStyle(
        SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
        start = 0,
        end = 9
    )
}

// Query active styles from TextFieldState
val currentStyles = state.textStyles

Selección de texto

Una nueva SelectionState API proporciona control programático y observabilidad sobre la selección de texto dentro de un SelectionContainer. Si se aloja un objeto SelectionState a través de rememberSelectionState() y se pasa a SelectionContainer, se expone selectedTexts como una lista reactiva de AnnotatedStrings y se proporcionan métodos como selectAll()clear(), select(TextRange)extendSelectionByWord().

Además, usa getSelectableTexts() para recuperar todos los elementos de texto seleccionables en orden de diseño y seleccionar texto en elementos componibles en SelectionContainer con un rango global. 

@Composable
fun ProgrammaticSelectionExample() {
    val selectionState = rememberSelectionState()

    Column {
        Button(
            onClick = { selectionState.selectAll() },
            modifier = Modifier.disableSelectionClearOnTap()
        ) {
            Text("Select All")
        }

        SelectionContainer(state = selectionState) {
            Text("Text content to be selected programmatically.")
        }
    }
}

Integración de Credential Manager

Los campos de texto de Compose ahora se integran de forma nativa con Credential Manager de Android (API 34+) a través del framework de Autofill (la API 34 se controla con androidx.credentials library). Si se adjunta la nueva propiedad semántica credentialRequest con CredentialRequestData, las entradas de texto pueden solicitar llaves de acceso, credenciales guardadas o solicitudes de acceso directamente dentro del flujo de entrada del usuario.

@Composable
fun LoginField(textFieldState: TextFieldState) {
    val credentialData = remember {
        CredentialRequestData(
            // Specify Credential Manager request options
        )
    }

    BasicTextField(
        state = textFieldState,
        modifier = Modifier.semantics {
            credentialRequest = credentialData
        }
    )
}

Otros cambios destacados:

  • Compatibilidad con la configuración de variación de fuentes en fuentes descargables.
  • Se habilitó el desplazamiento automático cuando se arrastra la selección de texto más allá de la ventana gráfica en SelectionContainer.
  • Se agregó compatibilidad con sonidos de interacción automática (clics y navegación de enfoque) a los componentes de Compose, con un nuevo SoundEffectOnInteraction elemento componible para permitir la exclusión. Ten en cuenta que, como consecuencia de este cambio, ahora se debe llamar a los objetos de escucha de clics semánticos desde el subproceso principal, lo que puede afectar una pequeña cantidad de casos de prueba.
  • KeyboardType ahora incluye DateTimeDateTime, y SignedDecimal.
  • BasicSecureTextField ahora usa TextObfuscationMode.System de forma predeterminada, mientras que RevealLastTyped sirve como anulación absoluta.

Mejoras en el diseño

Áreas con nombre en el diseño de cuadrícula

Ahora es más fácil compilar diseños 2D complejos con áreas con nombre en el @Experimental Grid componente. En lugar de administrar índices numéricos de columnas y filas en los elementos, puedes definir regiones semánticas en tu GridConfigurationScope y posicionar elementos componibles por nombre de área.

@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
    Grid(
        config = {
            area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
            area("sidebar", row = 1, column = 0)
            area("content", row = 1, column = 1)
            gap(16.dp)
        }
    ) {
        HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
        NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
        MainContentView(modifier = Modifier.gridItem(areaId = "content"))
    }
}

Para obtener más información, consulta la documentación.

Rendimiento

Al igual que con cada versión, seguimos invirtiendo en el rendimiento de Compose para garantizar que el framework te ayude a compilar apps atractivas y de alto rendimiento. En esta versión, nos enfocamos en mejorar el rendimiento de inicio y ahora vemos el tiempo hasta la visualización inicial (el tiempo que tarda una app en producir su primer fotograma) que es comparable a Views en nuestros benchmarks.

timetoinitialdisplay@2x _v2.png

Actualizaciones de pruebas y herramientas

Sincronización de pruebas

Compose 1.12 presenta nuevas APIs de prueba diseñadas para reducir los tiempos de ejecución de pruebas y eliminar la inestabilidad durante el muestreo de estado:

  • hasPendingWork: Verifica de forma pasiva si la IU tiene trabajo pendiente sin avanzar el reloj, lo que es ideal para los bucles de animación manuales.
  • runWithoutImplicitWait: Inhabilita temporalmente la sincronización implícita cuando se pasa por los fotogramas del reloj manual (p.ej., pruebas de animación).
@Test
fun testAnimationStateFast() {

composeTestRule.mainClock.autoAdvance = false
    
    while (composeTestRule.hasPendingWork()) {
        composeTestRule.mainClock.advanceTimeByFrame()
        composeTestRule.waitForIdle()
        
        composeTestRule.runOnUiThread {
            composeTestRule.runWithoutImplicitWait {
                // This is most effective when querying multiple nodes in a single frame. 
                // It prevents the redundant synchronization overhead that would 
                // otherwise occur on every individual query.
                val box1 = composeTestRule.onNodeWithTag("Box1").fetchSemanticsNode()
                val box2 = composeTestRule.onNodeWithTag("Box2").fetchSemanticsNode()
                
                assertThat(box1.boundsInRoot.right).isAtMost(box2.boundsInRoot.left)
            }
        }
    }
}

Otros cambios destacados:

  • La API de captureToImage ahora te permite capturar una ventana emergente o un diálogo junto con su ancla en un solo mapa de bits.
  • Se agregó onRootWithViewInteraction para limitar las búsquedas semánticas de Compose a vistas de Android específicas. Esto simplifica las pruebas de IUs híbridas, como RecyclerViews, sin requerir etiquetas de prueba únicas en el código de producción.
  • @PreviewWrapper anotaciones ahora se pueden aplicar a clases @MultiPreview personalizadas, lo que permite configuraciones de vista previa reutilizables (como temas personalizados) en varios componentes.

¡Feliz compilación!

Compose 1.12 hace que el desarrollo de apps sea más fácil y expresivo que nunca, con gradientes de malla, compatibilidad con una amplia gama de colores, fuentes variables descargables, integración de Credential Manager y herramientas de prueba más rápidas. Como siempre, valoramos tus comentarios, así que comparte tus opiniones sobre estos cambios o lo que te gustaría ver a continuación en nuestro seguimiento de errores. ¡Feliz compilación!

Escrito por:
Continuar leyendo