Gli elementi condivisi rendono le transizioni tra le schermate più fluide e coinvolgenti creando una connessione visiva che guida l'utente. Questa guida mostra come utilizzare le API degli elementi condivisi con le librerie Jetpack Navigation 3 e Navigation 2.
Lo snippet seguente include i composable DetailsScreen e HomeScreen che fungono da destinazioni tra cui gli utenti possono navigare. All'interno di ogni schermata, il
sharedElement modificatore viene utilizzato sia sull'immagine sia sul testo, in modo che
ognuno di questi elementi animi in modo indipendente tra le schermate.
@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
Per utilizzare le API degli elementi condivisi con Navigation 3, devi prima racchiudere la tua app's
NavDisplay in un SharedTransitionLayout. Puoi quindi passare il
fornito SharedTransitionScope ai composable della schermata.
Per AnimatedVisibilityScope, utilizza la
LocalNavAnimatedContentScope composizione locale che fornisce la
AnimatedContentScope da AnimatedContent che NavDisplay
utilizza internamente per animare tra le scene.
@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() }, ) } }) } }
Navigation 2
Per utilizzare le API degli elementi condivisi con Navigation 2, devi prima racchiudere
NavHost della tua app in un SharedTransitionLayout. Puoi quindi passare SharedTransitionScope fornito ai composable della schermata.
Il parametro content del builder composable utilizza
AnimatedContentScope come ricevitore, quindi puoi utilizzare this@composable per
fare riferimento a questo ambito.
@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() } ) } } } }
Indietro predittivo con elementi condivisi
Per utilizzare Indietro predittivo con elementi condivisi:
Tutte le versioni di Navigation 3 supportano Indietro predittivo. Per Navigation 2, usa la release
2.8.0-alpha02dinavigation-composeo versioni successive:[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) }Le animazioni di Indietro predittivo sono attivate per impostazione predefinita sui dispositivi con Android 15 (livello API 35) o versioni successive. Per i dispositivi con Android 14 (livello API 34), devi attivare l'impostazione Indietro predittivo nelle opzioni sviluppatore.
Se la tua app ha come target Android 14 o versioni precedenti, devi aggiungere
android:enableOnBackInvokedCallback="true"agli elementi<application>o specifici<activity>nel fileAndroidManifest.xml. Non hai bisogno di questo flag se la tua app ha come target Android 15 o versioni successive.<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application ... android:enableOnBackInvokedCallback="true"> </application> </manifest>