Skip to content

Instantly share code, notes, and snippets.

@zskamljic
Created May 30, 2021 08:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zskamljic/74a2aae3257197a8a8374f61ecfdc956 to your computer and use it in GitHub Desktop.
Save zskamljic/74a2aae3257197a8a8374f61ecfdc956 to your computer and use it in GitHub Desktop.
@Composable
fun CurvedScroll(
itemCount: Int,
item: @Composable (Int) -> Unit
) {
val scrollState = rememberScrollState()
// Added: to hold the size of the composable
var size by remember { mutableStateOf(IntSize.Zero) }
Box(modifier = Modifier.onSizeChanged { size = it }) {
Layout(
modifier = Modifier.verticalScroll(scrollState),
content = { repeat(itemCount) { item(it) } }
) { measurables, constraints ->
val itemSpacing = 16.dp.roundToPx()
// Added: to hold the height of children, we can already calculate the amount of space between them
var contentHeight = (itemCount - 1) * itemSpacing
// Updated: use index for special handling of first and last items
val placeables = measurables.mapIndexed { index, measurable ->
val placeable = measurable.measure(constraints)
// First and last items should be centered, meaning half of the view is above center line and half below
contentHeight += if (index == 0 || index == measurables.lastIndex) {
placeable.height / 2
} else {
placeable.height
}
placeable
}
// Updated: use the height of Box to allow for vertical centering, half the height comes before first item, half after the last
layout(constraints.maxWidth, size.height + contentHeight) {
// Calculate the value so that first item is centered vertically
val startOffset = size.height / 2 - placeables[0].height / 2
// All items should be placed according to it
var yPosition = startOffset
placeables.forEach { placeable ->
placeable.placeRelative(x = 0, y = yPosition)
yPosition += placeable.height + itemSpacing
}
}
}
}
}
@Preview
@Composable
fun CurvedScrollPreview() {
val items = listOf(
"One",
"Two",
"Three",
"Four",
"Five",
"Six"
)
CustomTheme {
Box(
modifier = Modifier
.background(Color.Gray)
.height(300.dp),
contentAlignment = Alignment.Center,
) {
CurvedScroll(items.count()) {
Text(text = items[it], style = MaterialTheme.typography.h4)
}
// Added as a helper, to see the vertical center and make sure views are at their proper positions
Divider(color = Color.Red)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment