Cómo migrar de Material 2.5 a Material 3 en Compose para Wear OS

Material 3 Expressive es la siguiente evolución de Material Design. Incluye temas, componentes y funciones de personalización actualizados, como el color dinámico.

En esta guía, se explica cómo migrar de la biblioteca de Wear Compose Material 2.5 (androidx.wear.compose) Jetpack a la biblioteca de Wear Compose Material 3 (androidx.wear.compose.material3) Jetpack para apps.

Enfoques

Para migrar el código de tu app de M2.5 a M3, sigue el mismo enfoque que se describe en la guía para teléfonos sobre la migración de Compose Material, en particular:

Dependencias

M3 tiene un paquete y una versión diferentes a M2.5:

M2.5

implementation("androidx.wear.compose:compose-material:1.4.0")

M3

implementation("androidx.wear.compose:compose-material3:1.6.0")

Consulta las versiones más recientes de M3 en la página de versiones de Wear Compose Material 3.

La versión 1.6.0 de la biblioteca de Wear Compose Foundation introdujo algunos componentes nuevos diseñados para funcionar con los componentes de Material 3. Del mismo modo, SwipeDismissableNavHost de la biblioteca de Wear Compose Navigation tiene una animación actualizada cuando se ejecuta en Wear OS 6 (nivel de API 36) o versiones posteriores. Cuando actualices a la versión de Wear Compose Material 3, te sugerimos que también actualices las bibliotecas de Wear Compose Foundation y Navigation:

implementation("androidx.wear.compose:compose-foundation:1.6.0")
implementation("androidx.wear.compose:compose-navigation:1.6.0")

Tema

En M2.5 y M3, el tema que admite composición se denomina MaterialTheme, pero los paquetes de importación y los parámetros difieren. En M3, se cambió el nombre del parámetro Colors a ColorScheme y se introdujo MotionScheme para implementar transiciones.

M2.5

import androidx.wear.compose.material.MaterialTheme

MaterialTheme(
    colors = AppColors,
    typography = AppTypography,
    shapes = AppShapes,
    content = content
)

M3

import androidx.wear.compose.material3.MaterialTheme
// ...
    MaterialTheme(
        colorScheme = ColorScheme(),
        typography = Typography(),
        shapes = Shapes(),
        motionScheme = MotionScheme.standard(),
        content = { /*content here*/ }
    )

Color

El sistema de color en M3 es significativamente diferente al de M2.5. La cantidad de parámetros de color aumentó, tienen nombres diferentes y se asignan de forma distinta a los componentes de M3. En Compose, esto se aplica a la clase Colors de M2.5, la clase ColorScheme de M3 y las funciones relacionadas:

M2.5

import androidx.wear.compose.material.Colors

val appColorScheme: Colors = Colors(
   // M2.5 Color parameters
)

M3

import androidx.wear.compose.material3.ColorScheme
// ...
    val appColorScheme: ColorScheme = ColorScheme(
        // M3 ColorScheme parameters
    )

En la siguiente tabla, se describen las diferencias clave entre M2.5 y M3:

M2.5 M3
Color Se cambió el nombre a ColorScheme.
13 colores 28 colores
N/A Nuevo tema de color dinámico
N/A Nuevos colores terciarios para una mayor expresión

Temas de color dinámico

Una nueva función de M3 es el tema de color dinámico. Si los usuarios cambian los colores de la cara del reloj, los colores de la IU también cambiarán para que coincidan.

Usa la función dynamicColorScheme para implementar el esquema de colores dinámico y proporcionar un defaultColorScheme como resguardo en caso de que el esquema de colores dinámico no esté disponible.

@Composable
fun myApp() {
    val dynamicColorScheme = dynamicColorScheme(LocalContext.current)
    MaterialTheme(colorScheme = dynamicColorScheme ?: myBrandColors) {}
}

internal val myBrandColors: ColorScheme = ColorScheme( /* Specify colors here */)

Tipografía

El sistema de tipografía en M3 es diferente al de M2.5 y tiene las siguientes características:

  • Nueve nuevos estilos de texto
  • Fuentes flexibles, que permiten personalizar las escalas de escritura para diferentes pesos, anchos y redondez
  • AnimatedText, que usa fuentes flexibles

M2.5

import androidx.wear.compose.material.Typography

val Typography = Typography(
   // M2.5 TextStyle parameters
)

M3

import androidx.wear.compose.material3.Typography

val Typography = Typography(
    // M3 TextStyle parameters
)

Fuentes flexibles

Las fuentes flexibles permiten a los diseñadores especificar el ancho y el grosor del tipo para tamaños específicos.

Estilos de texto

Los siguientes TextStyles están disponibles en M3. Varios componentes de M3 los emplean de forma predeterminada.

Tipografía TextStyle
Pantalla displayLarge, displayMedium, displaySmall
Título titleLarge, titleMedium, titleSmall
Etiqueta labelLarge, labelMedium, labelSmall
Cuerpo bodyLarge, bodyMedium, bodySmall, bodyExtraSmall
Numeral numeralExtraLarge, numeralLarge, numeralMedium, numeralSmall, numeralExtraSmall
Arc arcLarge, arcMedium, arcSmall

Forma

El sistema de forma en M3 es diferente al de M2.5. La cantidad de parámetros de forma aumentó, tienen nombres diferentes y se asignan, de forma distinta, a los componentes de M3. Los siguientes tamaños de formas están disponibles:

  • Extrapequeño
  • Pequeño
  • Mediano
  • Grande
  • Extragrande

En Compose, esto se aplica a la clase Shapes de M2 y la clase Shapes de M3:

M2.5

import androidx.wear.compose.material.Shapes

val Shapes = Shapes(
   // M2.5 Shapes parameters
)

M3

import androidx.wear.compose.material3.Shapes

val Shapes = Shapes(
    // M3 Shapes parameters
)

Usa la asignación de parámetros de formas de Cómo migrar de Material 2 a Material 3 en Compose como punto de partida.

Transformación de formas

M3 introduce el cambio de forma: Las formas ahora cambian en respuesta a las interacciones.

El comportamiento de transformación de formas está disponible como una variación en varios botones redondos. Consulta la siguiente lista de botones que admiten la transformación de formas:

Botones Función de transformación de formas
IconButton IconButtonDefaults.animatedShape anima el botón de ícono cuando se presiona.
IconToggleButton IconToggleButtonDefaults.animatedShape anima el botón de alternancia de ícono al presionarlo y
IconToggleButtonDefaults.variantAnimatedShapes anima el botón de alternancia de ícono al presionarlo y marcarlo o desmarcarlo.
TextButton TextButtonDefaults.animatedShape anima el botón de texto cuando se presiona.
TextToggleButton TextToggleButtonDefaults.animatedShapes anima el botón de alternancia de texto cuando se presiona, y TextToggleButtonDefaults.variantAnimatedShapes anima el botón de alternancia de texto cuando se presiona y se marca o desmarca.

Componentes y diseño

La mayoría de los componentes y diseños de M2.5 están disponibles en M3. Sin embargo, algunos componentes y diseños de M3 no existían en M2.5. Además, algunos componentes de M3 tienen más variaciones que sus equivalentes en M2.5.

Si bien algunos componentes requieren consideraciones especiales, se recomiendan las siguientes asignaciones de funciones como punto de partida:

Material 2.5 Material 3
androidx.wear.compose.material.dialog.Alert androidx.wear.compose.material3.AlertDialog
androidx.wear.compose.material.Button androidx.wear.compose.material3.IconButton o androidx.wear.compose.material3.TextButton
androidx.wear.compose.material.Card androidx.wear.compose.material3.Card
androidx.wear.compose.material.TitleCard androidx.wear.compose.material3.TitleCard
androidx.wear.compose.material.AppCard androidx.wear.compose.material3.AppCard
androidx.wear.compose.material.Checkbox Sin equivalente en M3; migra a androidx.wear.compose.material3.CheckboxButton o androidx.wear.compose.material3.SplitCheckboxButton
androidx.wear.compose.material.Chip androidx.wear.compose.material3.Button o
androidx.wear.compose.material3.OutlinedButton o
androidx.wear.compose.material3.FilledTonalButton o
androidx.wear.compose.material3.ChildButton
androidx.wear.compose.material.CompactChip androidx.wear.compose.material3.CompactButton
androidx.wear.compose.material.InlineSlider androidx.wear.compose.material3.Slider
androidx.wear.compose.material.LocalContentAlpha() Se quitó porque Text o Icon no lo usan en Material 3
androidx.wear.compose.material.PositionIndicator androidx.wear.compose.material3.ScrollIndicator
androidx.wear.compose.material.RadioButton Sin equivalente en M3; migra a androidx.wear.compose.material3.RadioButton o androidx.wear.compose.material3.SplitRadioButton
androidx.wear.compose.material.SwipeToRevealCard androidx.wear.compose.material3.SwipeToReveal
androidx.wear.compose.material.SwipeToRevealChip androidx.wear.compose.material3.SwipeToReveal
android.wear.compose.material.Scaffold androidx.wear.compose.material3.AppScaffold y androidx.wear.compose.material3.ScreenScaffold
androidx.wear.compose.material.SplitToggleChip No hay un equivalente en M3. Migra a androidx.wear.compose.material3.SplitCheckboxButton, androidx.wear.compose.material3.SplitSwitchButton o androidx.wear.compose.material3.SplitRadioButton.
androidx.wear.compose.material.Switch No hay un equivalente en M3. Migra a androidx.wear.compose.material3.SwitchButton o androidx.wear.compose.material3.SplitSwitchButton.
androidx.wear.compose.material.ToggleButton androidx.wear.compose.material3.IconToggleButton o androidx.wear.compose.material3.TextToggleButton
androidx.wear.compose.material.ToggleChip androidx.wear.compose.material3.CheckboxButton,
androidx.wear.compose.material3.RadioButton o
androidx.wear.compose.material3.SwitchButton
androidx.wear.compose.material.Vignette Se quitó porque no se incluye en el diseño expresivo de Material 3 para Wear OS

Esta es una lista completa de todos los componentes de Material 3:

Material 3 Componente equivalente de Material 2.5 (si no es nuevo en M3)
androidx.wear.compose.material3.AlertDialog androidx.wear.compose.material.dialog.Alert
androidx.wear.compose.material3.AnimatedPage Novedades
androidx.wear.compose.material3.AnimatedText Novedades
androidx.wear.compose.material3.AppScaffold android.wear.compose.material.Scaffold (con androidx.wear.compose.material3.ScreenScaffold)
androidx.wear.compose.material3.Button androidx.wear.compose.material.Chip
androidx.wear.compose.material3.ButtonGroup Novedades
androidx.wear.compose.material3.Card androidx.wear.compose.material.Card
androidx.wear.compose.material3.CheckboxButton androidx.wear.compose.material.ToggleChip con un control de activación de casilla de verificación
androidx.wear.compose.material3.ChildButton androidx.wear.compose.material.Chip (solo cuando no se requiere un fondo)
androidx.wear.compose.material3.CircularProgressIndicator androidx.wear.compose.material.CircularProgressIndicator
androidx.wear.compose.material3.CompactButton androidx.wear.compose.material.CompactChip
androidx.wear.compose.material3.ConfirmationDialog androidx.wear.compose.material.dialog.Confirmation
androidx.wear.compose.material3.curvedText androidx.wear.compose.material.curvedText
androidx.wear.compose.material3.DatePicker Novedades
androidx.wear.compose.material3.Dialog androidx.wear.compose.material.dialog.Dialog
androidx.wear.compose.material3.EdgeButton Novedades
androidx.wear.compose.material3.FadingExpandingLabel Novedades
androidx.wear.compose.material3.FilledTonalButton androidx.wear.compose.material.Chip cuando se requiere un fondo de botón tonal
androidx.wear.compose.material3.HorizontalPageIndicator androidx.wear.compose.material.HorizontalPageIndicator
androidx.wear.compose.material3.HorizontalPagerScaffold Novedades
androidx.wear.compose.material3.Icon androidx.wear.compose.material.Icon
androidx.wear.compose.material3.IconButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.IconToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.LevelIndicator Novedades
androidx.wear.compose.material3.LinearProgressIndicator Novedades
androidx.wear.compose.material3.ListHeader androidx.wear.compose.material.ListHeader
androidx.wear.compose.material3.ListSubHeader Novedades
androidx.wear.compose.material3.MaterialTheme androidx.wear.compose.material.MaterialTheme
androidx.wear.compose.material3.OpenOnPhoneDialog Novedades
androidx.wear.compose.material3.Picker androidx.wear.compose.material.Picker
androidx.wear.compose.material3.PickerGroup androidx.wear.compose.material.PickerGroup
androidx.wear.compose.material3.RadioButton androidx.wear.compose.material.ToggleChip con un control de activación de botón de selección
androidx.wear.compose.material3.ScreenScaffold android.wear.compose.material.Scaffold (con androidx.wear.compose.material3.AppScaffold)
androidx.wear.compose.material3.ScrollIndicator androidx.wear.compose.material.PositionIndicator
androidx.wear.compose.material3.scrollAway androidx.wear.compose.material.scrollAway
androidx.wear.compose.material3.SegmentedCircularProgressIndicator Novedades
androidx.wear.compose.material3.Slider androidx.wear.compose.material.InlineSlider
androidx.wear.compose.material3.SplitRadioButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitCheckboxButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.SplitSwitchButton androidx.wear.compose.material.SplitToggleChip
androidx.wear.compose.material3.Stepper androidx.wear.compose.material.Stepper
androidx.wear.compose.material3.SwipeToDismissBox androidx.wear.compose.material.SwipeToDismissBox
androidx.wear.compose.material3.SwipeToReveal androidx.wear.compose.material.SwipeToRevealCard y androidx.wear.compose.material.SwipeToRevealChip
androidx.wear.compose.material3.SwitchButton androidx.wear.compose.material.ToggleChip con un control de activación de interruptor
androidx.wear.compose.material3.Text androidx.wear.compose.material.Text
androidx.wear.compose.material3.TextButton androidx.wear.compose.material.Button
androidx.wear.compose.material3.TextToggleButton androidx.wear.compose.material.ToggleButton
androidx.wear.compose.material3.TimeText androidx.wear.compose.material.TimeText
androidx.wear.compose.material3.VerticalPagerScaffold Novedades

Por último, se incluye una lista de algunos componentes relevantes de la biblioteca de Wear Compose Foundation:

Wear Compose Foundation 1.6.0
androidx.wear.compose.foundation.hierarchicalFocusGroup Se usa para anotar elementos componibles en una aplicación, hacer un seguimiento de la parte activa de la composición y coordinar el enfoque.
androidx.wear.compose.foundation.pager.HorizontalPager Es un paginador de desplazamiento horizontal, creado sobre los componentes de Compose Foundation con mejoras específicas para Wear que permiten mejorar el rendimiento y el cumplimiento de los lineamientos de Wear OS.
androidx.wear.compose.foundation.pager.VerticalPager Es un paginador de desplazamiento vertical compilado sobre los componentes de Compose Foundation con mejoras específicas para Wear que permiten mejorar el rendimiento y el cumplimiento de los lineamientos de Wear OS.
androidx.wear.compose.foundation.lazy.TransformingLazyColumn Se puede usar en lugar de ScalingLazyColumn para agregar efectos de transformación de desplazamiento a cada elemento.

Botones

Los botones en M3 son diferentes a los de M2.5. El chip M2.5 se reemplazó por el botón. La implementación de Button proporciona valores predeterminados para Text, maxLines y textAlign. Esos valores predeterminados se pueden anular en el elemento Text.

M2.5

import androidx.wear.compose.material.Chip

//M2.5 Buttons
Chip(...)
CompactChip(...)
Button(...)

M3

//M3 Buttons
Button(onClick = { }){}
CompactButton(onClick = { }){}
IconButton(onClick = { }){}
TextButton(onClick = { }){}

M3 también incluye nuevas variaciones de botones. Consulta la descripción general de la referencia de la API de Compose Material 3.

M3 presenta un nuevo botón: EdgeButton. EdgeButton está disponible en 4 tamaños diferentes: extrapequeño, pequeño, mediano y grande. La implementación de EdgeButton proporciona un valor predeterminado para maxLines según el tamaño, que se puede personalizar.

Si usas TransformingLazyColumn o ScalingLazyColumn, pasa EdgeButton a ScreenScaffold para que se transforme y cambie de forma con el desplazamiento, en lugar de agregar EdgeButton como el elemento final de la lista. Consulta el siguiente código para verificar cómo usar EdgeButton con ScreenScaffold y TransformingLazyColumn.

val state = rememberTransformingLazyColumnState()
ScreenScaffold(
    scrollState = state,
    contentPadding =
        rememberResponsiveColumnPadding(
            first = ColumnItemType.ListHeader
        ),
    edgeButton = {
        EdgeButton(
            onClick = { }
        ) {
            Text(stringResource(R.string.show))
        }
    }
){ contentPadding ->
    TransformingLazyColumn(state = state, contentPadding = contentPadding,){
        // additional code here
    }
}

Scaffold

El andamiaje en M3 es diferente al de M2.5. En M3, AppScaffold y el nuevo elemento ScreenScaffold componible reemplazaron a Scaffold. AppScaffold y ScreenScaffold diseñan la estructura de una pantalla y coordinan las transiciones de los componentes ScrollIndicator y TimeText.

AppScaffold permite que los elementos de pantalla estáticos, como TimeText, permanezcan visibles durante las transiciones en la app, como el deslizamiento para descartar. ​​Proporciona una ranura para el contenido principal de la aplicación, que suele proporcionarse a través de un componente de navegación, como SwipeDismissableNavHost.

Declaras un AppScaffold para la actividad y usas un ScreenScaffold para cada pantalla. AppScaffold agrega un componente TimeTextpredeterminado a las pantallas. Puedes anularlo si quieres personalizarlo con el parámetro timeText.

M2.5

import androidx.wear.compose.material.Scaffold

Scaffold {...}

M3

    AppScaffold {
        val navController = rememberSwipeDismissableNavController()
        SwipeDismissableNavHost(
            navController = navController,
            startDestination = "message_list"
        ) {
            composable("message_list") {
                MessageList(onMessageClick = { id ->
                    navController.navigate("message_detail/$id")
                })
            }
            composable("message_detail/{id}") {
                MessageDetail(id = it.arguments?.getString("id")!!)
            }
        }
    }
}

// Implementation of one of the screens in the navigation
@Composable
fun MessageDetail(id: String) {
    // .. Screen level content goes here
    val scrollState = rememberTransformingLazyColumnState()

    val padding = rememberResponsiveColumnPadding(
        first = ColumnItemType.BodyText
    )

    ScreenScaffold(
        scrollState = scrollState,
        contentPadding = padding
    ) { scaffoldPaddingValues ->
        // Screen content goes here
        // ...

Si usas un HorizontalPager con HorizontalPagerIndicator, puedes migrar a HorizontalPagerScaffold. HorizontalPagerScaffold se coloca dentro de un AppScaffold. AppScaffold y HorizontalPagerScaffold diseñan la estructura de un Pager y coordinan las transiciones de los componentes HorizontalPageIndicator y TimeText.

HorizontalPagerScaffold muestra el HorizontalPageIndicator en el centro de la pantalla de forma predeterminada y coordina la visualización y el ocultamiento de TimeText y HorizontalPageIndicator según si se está paginando el Pager. Esto lo determina el PagerState.

También hay un nuevo componente AnimatedPage, que anima una página dentro de un Pager con un efecto de escala y de scrim según su posición.

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })
    val columnState = rememberTransformingLazyColumnState()
    val contentPadding = rememberResponsiveColumnPadding(
        first = ColumnItemType.ListHeader,
        last = ColumnItemType.BodyText,
    )
    HorizontalPagerScaffold(pagerState = pagerState) {
        HorizontalPager(
            state = pagerState,
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                ScreenScaffold(
                    scrollState = columnState,
                    contentPadding = contentPadding
                ) { contentPadding ->
                    TransformingLazyColumn(
                        state = columnState,
                        contentPadding = contentPadding
                    ) {
                        item {
                            ListHeader(
                                modifier = Modifier.fillMaxWidth()
                            ) {
                                Text(text = "Pager sample")
                            }
                        }
                        item {
                            if (page == 0) {
                                Text(text = "Page #$page. Swipe right")
                            }
                            else{
                                Text(text = "Page #$page. Swipe left and right")
                            }
                        }
                    }
                }

            }
        }
    }
}

Por último, M3 introduce un VerticalPagerScaffold que sigue el mismo patrón que el HorizontalPagerScaffold:

AppScaffold {
    val pagerState = rememberPagerState(pageCount = { 10 })

    VerticalPagerScaffold(pagerState = pagerState) {
        VerticalPager(
            state = pagerState
        ) { page ->
            AnimatedPage(pageIndex = page, pagerState = pagerState) {
                ScreenScaffold {
                    ///…
                }
            }
        }
    }
}

Marcador de posición

Hay algunos cambios en la API entre M2.5 y M3. Placeholder.PlaceholderDefaults ahora proporciona dos modificadores:

  • Modifier.placeholder, que se dibuja en lugar del contenido que aún no se cargó
  • Un efecto de brillo de marcador de posición Modifier.placeholderShimmer que proporciona un efecto de brillo de marcador de posición que se ejecuta en un bucle de animación mientras se espera que se carguen los datos.

Consulta la siguiente tabla para ver los cambios adicionales en el componente Placeholder.

M2.5 M3
PlaceholderState.startPlaceholderAnimation Se quitó
PlaceholderState.placeholderProgression Se quitó
PlaceholderState.isShowContent Se cambió el nombre a !PlaceholderState.isVisible.
PlaceholderState.isWipeOff Se quitó
PlaceholderDefaults.painterWithPlaceholderOverlayBackgroundBrush Se quitó
PlaceholderDefaults.placeholderBackgroundBrush Se quitó
PlaceholderDefaults.placeholderChipColors Se quitó

SwipeDismissableNavHost

SwipeDismissableNavHost forma parte de wear.compose.navigation. Cuando este componente se usa con M3, el MaterialTheme de M3 actualiza LocalSwipeToDismissBackgroundScrimColor y LocalSwipeToDismissContentScrimColor.

TransformingLazyColumn

TransformingLazyColumn forma parte de wear.compose.lazy.foundation y agrega compatibilidad con animaciones de cambio de forma y ajuste de escala en los elementos de la lista durante el desplazamiento, lo que mejora la experiencia del usuario. Se recomienda que las apps migren de ScalingLazyColumn a TransformingLazyColumn

De manera similar a ScalingLazyColumn, proporciona rememberTransformingLazyColumnState() para crear un TransformingLazyColumnState que se recuerde en todas las composiciones.

Para agregar animaciones de escalamiento y transformación, agrega lo siguiente a cada elemento de la lista:

  • Modifier.transformedHeight, que te permite calcular la altura transformada de los elementos con un TransformationSpec, puedes usar rememberTransformationSpec(), a menos que necesites más personalización.
  • Un SurfaceTransformation

Para verificar que el padding sea correcto en la parte superior e inferior de la lista, usa el modificador minimumVerticalContentPadding.

val columnState = rememberTransformingLazyColumnState()
val transformationSpec = rememberTransformationSpec()
ScreenScaffold(
    scrollState = columnState
) { contentPadding ->
    TransformingLazyColumn(
        state = columnState,
        contentPadding = contentPadding
    ) {
        item {
            ListHeader(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding),
                transformation = SurfaceTransformation(transformationSpec)
            ) {
                Text(text = "Header")
            }
        }
        // ... other items
        item {
            Button(
                modifier = Modifier
                    .fillMaxWidth()
                    .transformedHeight(this, transformationSpec)
                    .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding),
                transformation = SurfaceTransformation(transformationSpec),
                onClick = { /* ... */ },
                icon = {
                    Icon(
                        imageVector = Icons.Default.Build,
                        contentDescription = "build",
                    )
                },
            ) {
                Text(
                    text = "Build",
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                )
            }
        }
    }
}

Para obtener más información sobre la migración de M2.5 a M3 en Compose, consulta los siguientes recursos adicionales.

Ejemplos

Referencia de la API y código fuente

Diseño