Skip to content

Instantly share code, notes, and snippets.

@vganin
Last active September 1, 2024 19:19
Show Gist options
  • Save vganin/081b535f0c21b6fcf43c7a71bde91bdd to your computer and use it in GitHub Desktop.
Save vganin/081b535f0c21b6fcf43c7a71bde91bdd to your computer and use it in GitHub Desktop.
[DEPRECATED, use official FlowRow instead] Jetpack Compose simple flex-wrap container
@Composable
fun FlowRow(
horizontalGap: Dp = 0.dp,
verticalGap: Dp = 0.dp,
alignment: Alignment.Horizontal = Alignment.Start,
content: @Composable () -> Unit,
) = Layout(content = content) { measurables, constraints ->
val horizontalGapPx = horizontalGap.toPx().roundToInt()
val verticalGapPx = verticalGap.toPx().roundToInt()
val rows = mutableListOf<Row>()
var rowConstraints = constraints
var rowPlaceables = mutableListOf<Placeable>()
measurables.forEach { measurable ->
val placeable = measurable.measure(Constraints())
if (placeable.measuredWidth !in rowConstraints.minWidth..rowConstraints.maxWidth) {
rows += Row(rowPlaceables, horizontalGapPx)
rowConstraints = constraints
rowPlaceables = mutableListOf()
}
val consumedWidth = placeable.measuredWidth + horizontalGapPx
rowConstraints = rowConstraints.offset(horizontal = -consumedWidth)
rowPlaceables.add(placeable)
}
rows += Row(rowPlaceables, horizontalGapPx)
val width = constraints.maxWidth
val height = (rows.sumBy { row -> row.height } + (rows.size - 1) * verticalGapPx)
.coerceAtMost(constraints.maxHeight)
layout(width, height) {
var y = 0
rows.forEach { row ->
val offset = alignment.align(row.width, width, layoutDirection)
var x = offset
row.placeables.forEach { placeable ->
placeable.placeRelative(x, y)
x += placeable.width + horizontalGapPx
}
y += row.height + verticalGapPx
}
}
}
private class Row(
val placeables: List<Placeable>,
val horizontalGapPx: Int,
) {
val width by lazy(mode = LazyThreadSafetyMode.NONE) {
placeables.sumBy { it.width } + (placeables.size - 1) * horizontalGapPx
}
val height by lazy(mode = LazyThreadSafetyMode.NONE) {
placeables.maxOfOrNull { it.height } ?: 0
}
}
@Composable
private fun Preview(alignment: Alignment.Horizontal) {
Box(Modifier.width(100.dp)) {
FlowRow(
horizontalGap = 8.dp,
verticalGap = 8.dp,
alignment = alignment,
) {
repeat(17) { index ->
Text(text = index.toString())
}
}
}
}
@Preview
@Composable
private fun PreviewAlignStart() = Preview(alignment = Alignment.Start)
@Preview
@Composable
private fun PreviewAlignCenter() = Preview(alignment = Alignment.CenterHorizontally)
@Preview
@Composable
private fun PreviewAlignEnd() = Preview(alignment = Alignment.End)
@austinevick
Copy link

I want to say that I am highly grateful to you for this solution. I spent over 8hrs looking for a custom wrap widget in Compose not untill I found your repo. Thank you very much for building this solution.

@shashank-msra
Copy link

https://developer.android.com/develop/ui/compose/layouts/flow
Dropping link for now available official support in compose.

@vganin
Copy link
Author

vganin commented Sep 1, 2024

https://developer.android.com/develop/ui/compose/layouts/flow Dropping link for now available official support in compose.

Thanks! I'll update the description with the pointer towards official implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment