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 componibili DetailsScreen e HomeScreen che fungono da destinazioni tra cui gli utenti possono spostarsi. All'interno di ogni schermata, il modificatore sharedElement viene utilizzato sia sull'immagine che sul testo, in modo che ognuno di questi elementi si 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, ) ) } } } } }
Navigazione 3
Per utilizzare le API degli elementi condivisi con Navigation 3, devi prima aggregare NavDisplay della tua app in un SharedTransitionLayout. Puoi quindi passare l'oggetto SharedTransitionScope fornito ai composabili dello schermo.
Per AnimatedVisibilityScope, utilizza la composizione locale LocalNavAnimatedContentScope che fornisce AnimatedContentScope da AnimatedContent che NavDisplay utilizza internamente per l'animazione 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() }, ) } }) } }
Navigazione 2
Per utilizzare le API degli elementi condivisi con Navigation 2, devi prima inserire NavHost della tua app in un SharedTransitionLayout. Puoi quindi passare l'oggetto SharedTransitionScope fornito ai composabili 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 la funzionalità Indietro predittivo con gli elementi condivisi, segui questi passaggi:
Tutte le versioni di Navigation 3 supportano l'Indietro predittivo. Per Navigation 2, utilizza la versione
2.8.0-alpha02dinavigation-composeo una versione successiva:[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 Indietro predittivo sono attive 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"all'elemento<application>o a elementi<activity>specifici nel fileAndroidManifest.xml. Questo flag non è necessario 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>