Los elementos compartidos hacen que las transiciones entre pantallas sean más fluidas y atractivas, ya que crean una conexión visual que guía al usuario. En esta guía, se muestra cómo usar las APIs de elementos compartidos con las bibliotecas de Jetpack Navigation 3 y Navigation 2.
En el siguiente fragmento, se incluyen los elementos de componibilidad DetailsScreen y HomeScreen que sirven como destinos entre los que los usuarios pueden navegar. Dentro de cada pantalla, se usa el modificador sharedElement tanto en la imagen como en el texto para que cada uno de esos elementos se anime de forma independiente entre las pantallas.
@Composable fun DetailsScreen( id: Int, snack: Snack, sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope, onBackPressed: () -> Unit ) { with(sharedTransitionScope) { Column( modifier = Modifier .fillMaxSize() .clickable { onBackPressed() }, ) { Image( painterResource(id = snack.image), contentDescription = snack.description, contentScale = ContentScale.Crop, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "image-$id"), animatedVisibilityScope = animatedVisibilityScope ) .aspectRatio(1f) .fillMaxWidth() ) Text( text = snack.name, fontSize = 18.sp, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "text-$id"), animatedVisibilityScope = animatedVisibilityScope ) .fillMaxWidth(), ) } } } @Composable fun HomeScreen( sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope, onItemClick: (Int) -> Unit, ) { LazyColumn( modifier = Modifier .fillMaxSize() .padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { itemsIndexed(listSnacks) { index, item -> Row( modifier = Modifier .fillMaxWidth() .clickable { onItemClick(index) }, ) { Spacer(modifier = Modifier.width(8.dp)) with(sharedTransitionScope) { Image( painterResource(id = item.image), contentDescription = item.description, contentScale = ContentScale.Crop, modifier = Modifier .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "image-$index"), animatedVisibilityScope = animatedVisibilityScope ) .size(100.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( item.name, fontSize = 18.sp, modifier = Modifier .align(Alignment.CenterVertically) .sharedElement( sharedTransitionScope.rememberSharedContentState(key = "text-$index"), animatedVisibilityScope = animatedVisibilityScope, ) ) } } } } }
Navigation 3
Para usar las APIs de elementos compartidos con Navigation 3, primero debes unir el NavDisplay de tu app en un SharedTransitionLayout. Luego, puedes pasar el SharedTransitionScope proporcionado a los elementos componibles de la pantalla.
Para AnimatedVisibilityScope, usa la composición local LocalNavAnimatedContentScope que proporciona AnimatedContentScope de AnimatedContent que NavDisplay usa de forma interna para animar entre escenas.
@Composable fun SharedElement_Nav3() { SharedTransitionLayout { val backStack = rememberNavBackStack(HomeRoute) // Note: NavDisplay accepts a `sharedTransitionScope` parameter, which is used to animate // NavEntry instances between scenes. This parameter *isn't* required for shared element // or shared bounds transitioning elements between different NavEntry, as demonstrated in // this sample. // See https://developer.android.com/guide/navigation/navigation-3/animate-destinations#transition-nav-entries NavDisplay( modifier = Modifier.safeDrawingPadding(), backStack = backStack, entryProvider = entryProvider { entry<HomeRoute> { HomeScreen( sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = LocalNavAnimatedContentScope.current, onItemClick = { backStack.add(DetailsRoute(it)) }) } entry<DetailsRoute> { detailsRoute -> val id = detailsRoute.item val snack = listSnacks[id] DetailsScreen( id = id, snack = snack, sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = LocalNavAnimatedContentScope.current, onBackPressed = { backStack.removeLastOrNull() }, ) } }) } }
Navegación 2
Para usar las APIs de elementos compartidos con Navigation 2, primero debes unir el NavHost de tu app en un SharedTransitionLayout. Luego, puedes pasar el SharedTransitionScope proporcionado a los elementos componibles de la pantalla.
El parámetro content del compilador composable usa
AnimatedContentScope como receptor, por lo que puedes usar this@composable para
hacer referencia a ese alcance.
@Composable fun SharedElement_Nav2() { SharedTransitionLayout { val navController = rememberNavController() NavHost( navController = navController, startDestination = "home", modifier = Modifier.safeDrawingPadding() ) { composable("home") { HomeScreen( sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = this@composable, onItemClick = { navController.navigate("details/$it") }) } composable( "details/{item}", arguments = listOf(navArgument("item") { type = NavType.IntType }) ) { backStackEntry -> val id = backStackEntry.arguments?.getInt("item") ?: 0 val snack = listSnacks[id] DetailsScreen( id = id, snack = snack, sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = this@composable, onBackPressed = { navController.popBackStack() } ) } } } }
Atrás predictivo con elementos compartidos
Para usar el gesto atrás predictivo con elementos compartidos, sigue estos pasos:
Todas las versiones de Navigation 3 admiten el gesto atrás predictivo. Para Navigation 2, usa la versión
2.8.0-alpha02denavigation-composeo una posterior:[versions] androidx-navigation = "2.8.0-alpha02" # Or newer [libraries] androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }dependencies { implementation(libs.androidx.navigation.compose) }Las animaciones de atrás predictivo están habilitadas de forma predeterminada en los dispositivos que ejecutan Android 15 (nivel de API 35) o versiones posteriores. En los dispositivos que ejecutan Android 14 (nivel de API 34), debes habilitar el parámetro de configuración de atrás predictivo en las opciones para desarrolladores.
Si tu app se orienta a Android 14 o versiones anteriores, debes agregar
android:enableOnBackInvokedCallback="true"al elemento<application>o a elementos<activity>específicos en tu archivoAndroidManifest.xml. No necesitas este parámetro si tu app se orienta a Android 15 o versiones posteriores.<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ... android:enableOnBackInvokedCallback="true"> </application> </manifest>