Destination this library is life saver for maintaining screens in compose as it removed lots of boiler plate but now i want to use the shared element transition with it and i am struggling to build it as what should i pass in the parameter.
We can pass string, boolean, serialized class but what is required to be passed down to next screen for transition animation
You can pass Int
, String
, Double
, or Float
or any class as long as rememberSharedContentState(key: Any)
keys match in sharedElement
or sharedBounds
in both screens.
But using unique keys are suggested by google
When working with complex shared elements, it is a good practice to create a key that is not a string, because strings can be error prone to match. Each key must be unique for matches to occur. For example, in Jetsnack we have the following shared elements:
data class SnackSharedElementKey(
val snackId: Long,
val origin: String,
val type: SnackSharedElementType
)
enum class SnackSharedElementType {
Bounds,
Image,
Title,
Tagline,
Background
}
@Composable
fun SharedElementUniqueKey() {
// ...
Box(
modifier = Modifier
.sharedElement(
rememberSharedContentState(
key = SnackSharedElementKey(
snackId = 1,
origin = "latest",
type = SnackSharedElementType.Image
)
),
animatedVisibilityScope = this@AnimatedVisibility
)
)
// ...
}
I made a sample with String keys that transitions from LazyColumn to HorizontalPager with navigation but as you can see neither screen are dependent of NavController.
@Composable
private fun DetailsScreen(
snack: SnackItem,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onClick: (String) -> Unit,
) {
var selectedIndex by remember {
mutableStateOf(listSnacks.indexOf(snack).coerceAtLeast(0))
}
with(sharedTransitionScope) {
val pagerState = rememberPagerState(
initialPage = selectedIndex,
pageCount = {
listSnacks.size
}
)
HorizontalPager(
state = pagerState,
pageSpacing = 16.dp
) { page ->
selectedIndex = page
val currentSnack = listSnacks[page]
Column(
Modifier
.fillMaxSize()
.clickable(
interactionSource = remember {
MutableInteractionSource()
},
indication = null
) {
onClick("home")
}
) {
Image(
painterResource(id = currentSnack.image),
contentDescription = currentSnack.description,
contentScale = ContentScale.Crop,
modifier = Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "image-$selectedIndex"),
animatedVisibilityScope = animatedContentScope
)
.aspectRatio(1f)
.fillMaxWidth()
)
Text(
currentSnack.name, fontSize = 38.sp,
modifier =
Modifier
.sharedBounds(
sharedTransitionScope.rememberSharedContentState(key = "text-$selectedIndex"),
animatedVisibilityScope = animatedContentScope,
)
)
}
}
}
}
@Composable
private fun HomeScreen(
scrollState: LazyListState,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
onClick: (String) -> Unit,
) {
var isDetailSelected by remember {
mutableStateOf(false)
}
with(sharedTransitionScope) {
Column {
TopAppBar(
title = {
Text("Title")
},
backgroundColor = Color.Black,
contentColor = Color.White,
modifier = if (isDetailSelected) Modifier
else Modifier.renderInSharedTransitionScopeOverlay()
)
LazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(8.dp),
state = scrollState,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
itemsIndexed(listSnacks) { index, item ->
Row(
Modifier.clickable(
interactionSource = remember {
MutableInteractionSource()
},
indication = null
) {
isDetailSelected = true
onClick("details/$index")
}
) {
Spacer(modifier = Modifier.width(8.dp))
Image(
painterResource(id = item.image),
contentDescription = item.description,
contentScale = ContentScale.Crop,
modifier = Modifier
.sharedElement(
sharedTransitionScope.rememberSharedContentState(key = "image-$index"),
animatedVisibilityScope = animatedContentScope
)
.size(120.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
item.name, fontSize = 18.sp,
modifier = Modifier
.align(Alignment.CenterVertically)
.sharedBounds(
sharedTransitionScope.rememberSharedContentState(key = "text-$index"),
animatedVisibilityScope = animatedContentScope
)
)
}
}
}
}
}
}