I would like to have a normal FlowRow layout. But ...
# compose
t
I would like to have a normal FlowRow layout. But when the size of one child element is bigger than the width of the FlowRow i would like to be able to scroll horizontally. Is it possible or do i have to create a custom layout for this?
z
So the flow row wouldn’t wrap if any child doesn’t fit?
t
No it should wrap normally. But when one child is bigger than the maximum available space it should scroll horizontally. Btw. I try to replicate the layout of the Android Studio Compose preview grid view.
So i do have some more or less working code:
Copy code
@Composable
fun SmartScrollableFlowRow(
    modifier: Modifier = Modifier,
    horizontalPadding: Dp = 4.dp,
    verticalPadding: Dp = 4.dp,
    scrollState: ScrollState,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier.horizontalScroll(scrollState),
        content = {
            content()
        },
    ) { measurables, constraints ->
        val paddingX = horizontalPadding.roundToPx()
        val paddingY = verticalPadding.roundToPx()
        var largestChildWidth = 0
        val placeables = measurables.map { measurable ->
            val placeable = measurable.measure(constraints)
            largestChildWidth = max(largestChildWidth, placeable.width)
            placeable
        }
        val width = max(scrollState.viewportSize, largestChildWidth)
        var posX = 0
        var posY = 0
        var maxRowHeight = 0
        var maxRowWidth = 0
        var heightSum = 0
        val offset = placeables.map {
            if (posX > 0 && (posX + it.width) > width) {
                //Next row
                posX = 0
                posY += paddingY + maxRowHeight
                maxRowHeight = 0
            }
            val pos = IntOffset(posX, posY)
            maxRowHeight = max(maxRowHeight, it.height)
            maxRowWidth = max(maxRowWidth, posX + it.width)
            heightSum = posY + it.height
            posX += it.width + paddingX
            pos
        }

        layout(maxRowWidth, heightSum) {
            placeables.forEachIndexed { index, placeable ->
                placeable.place(offset[index])
            }
        }
    }
}
Unfortunately my problem is much more complexe. In this picture my problem is this preview at the bottom it is bigger than the available space. So i would like to show a scrollbar.
Ok i think i found a better solution with this layout modifier:
Copy code
fun Modifier.intrinsicScrollModifier(scrollState: ScrollState) = horizontalScroll(scrollState) then object : LayoutModifier {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val intrinsic = measurable.minIntrinsicWidth(constraints.maxHeight)
        val maxViewport = max(constraints.minWidth, scrollState.viewportSize)
        val innerConstraints = constraints.copy(
            maxWidth = max(maxViewport, intrinsic)
        )
        val placeable = measurable.measure(innerConstraints)
        val width = max(placeable.width, intrinsic)
        return layout(width, placeable.height) {
            placeable.place(0, 0)
        }
    }
}
Usage looks than as follows:
Copy code
val scrollState = rememberScrollState()
FlowRow(
    modifier = Modifier.fillMaxWidth()
        .intrinsicScrollModifier(scrollState)
) {
    repeat(5) {
        Text(
            modifier = Modifier.requiredSize(180.dp, 50.dp)
                .background(Color.LightGray),
            text = "Test $it"
        )
    }
}