Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save KlassenKonstantin/51836ba84634a3a96ef6cf9aef88dd50 to your computer and use it in GitHub Desktop.
Save KlassenKonstantin/51836ba84634a3a96ef6cf9aef88dd50 to your computer and use it in GitHub Desktop.
A really simple grid-to-full-screen transition using Shared Elements
data class ColorItemState(
val id: Int,
val color: Color,
val name: String,
)
val CoolColors = listOf(
ColorItemState(
id = 1,
color = Color(0xFFEF4444),
name = "Red"
),
ColorItemState(
id = 2,
color = Color(0xFFEAB308),
name = "Yellow"
),
ColorItemState(
id = 3,
color = Color(0xFF22C55E),
name = "Green"
),
ColorItemState(
id = 4,
color = Color(0xFF3B82F6),
name = "Blue"
),
ColorItemState(
id = 5,
color = Color(0xFFA855F7),
name = "Purple"
),
)
data class ContainerTransformState(
val selectedItemId: Int? = null,
val colors: List<ColorItemState> = emptyList()
) {
val selectedItem = colors.find { it.id == selectedItemId }
}
@Composable
private fun rememberContainerTransformState(colors: List<ColorItemState>): MutableState<ContainerTransformState> {
return remember { mutableStateOf(ContainerTransformState(colors = colors)) }
}
@Preview
@Composable
fun ContainerTransformExample() {
var state by rememberContainerTransformState(
colors = CoolColors
)
// var scene by remember { mutableStateOf(Scene.Grid) }
val transition = updateTransition(state.selectedItem)
SharedTransitionLayout(
modifier = Modifier.fillMaxSize()
) {
transition.AnimatedContent(
transitionSpec = {
fadeIn(
animationSpec = tween(
durationMillis = DURATION_EXTRA_LONG,
easing = EmphasizedEasing
)
).togetherWith(
fadeOut(
animationSpec = tween(
durationMillis = DURATION_EXTRA_LONG,
easing = EmphasizedEasing
)
)
)
},
) { selectedItem ->
when {
selectedItem == null -> {
LazyVerticalGrid(
modifier = Modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.statusBars),
columns = GridCells.Fixed(2),
contentPadding = PaddingValues(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
items(state.colors, key = { it.id }) { item ->
val cornerSize by transition.animateDp(
transitionSpec = {
tween(durationMillis = DURATION_EXTRA_LONG, easing = EmphasizedEasing)
}
) { selectedItem ->
when {
selectedItem == item -> 0.dp
else -> 16.dp
}
}
Box(
modifier = Modifier
.sharedElement(
state = rememberSharedContentState(key = item.id),
animatedVisibilityScope = this@AnimatedContent,
boundsTransform = { _, _ ->
tween(durationMillis = DURATION_EXTRA_LONG, easing = EmphasizedEasing)
}
)
.fillMaxWidth()
.height(250.dp)
.clip(RoundedCornerShape(cornerSize))
.background(color = item.color)
.clickable {
state = state.copy(
selectedItemId = item.id
)
}
.padding(8.dp)
) {
Text(
modifier = Modifier
.align(Alignment.BottomCenter)
.renderInSharedTransitionScopeOverlay(
renderInOverlay = { item.id == state.selectedItemId }
)
.animateEnterExit(
enter = fadeIn(tween(delayMillis = 200)) + slideInVertically(
animationSpec = tween(delayMillis = 200),
initialOffsetY = { it / 2 }
),
exit = fadeOut(tween(durationMillis = 200))
),
text = item.name,
fontSize = 18.sp,
color = Color.White
)
}
}
}
}
else -> {
BackHandler {
state = state.copy(
selectedItemId = null
)
}
val cornerSize by transition.animateDp(
transitionSpec = {
tween(durationMillis = DURATION_EXTRA_LONG, easing = EmphasizedEasing)
}
) { selectedItem ->
when {
selectedItem == null -> 16.dp
else -> 0.dp
}
}
Box(
modifier = Modifier
.sharedElement(
state = rememberSharedContentState(key = selectedItem.id),
animatedVisibilityScope = this@AnimatedContent,
boundsTransform = { _, _ ->
tween(durationMillis = DURATION_EXTRA_LONG, easing = EmphasizedEasing)
},
)
.fillMaxSize()
.clip(RoundedCornerShape(cornerSize))
.background(
color = selectedItem.color
)
.windowInsetsPadding(WindowInsets.statusBars)
) {
}
}
}
}
}
}
const val DURATION_EXTRA_LONG = 1000
private val emphasizedPath = Path().apply {
moveTo(0f, 0f)
cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.04f)
cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f)
}
val emphasized = PathInterpolator(emphasizedPath)
val EmphasizedEasing = Easing { emphasized.getInterpolation(it) }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment