Skip to content

Instantly share code, notes, and snippets.

@vganin

vganin/FlexRow.kt

Last active May 5, 2021
Embed
What would you like to do?
Implementation of Jetpack Compose FlowRow (i.e. simple horizontal flexbox with "flex-wrap: wrap" if you may). This is NOT a copy of deprecated (and further removed) FlowRow from library
@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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment