I have a custom Table layout that looks like the i...
# compose
I have a custom Table layout that looks like the image below, it measures & places cells based on the following policy: • For each row, all cells must have the same height, this height is the max cell height of the row's cells height • Similarly, For each column, all cells must have the same width, and this width is the max cell width of the column's cells widths I'm primarily using Intrinsics to query children sizes for every row or column without doing actual measurement to do the measurement later based on the calculated size. this works fine but as you can see in the image below as the table grows in size & more cells are added, the user should be able to scroll horizontally & vertically to see the rest of the table. but, when I try to add
modifier to the Table layout, it results in infinite scrolling & eventually this exception is thrown. More in thread 🧵
Copy code
java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
                                                                                                    	at androidx.compose.foundation.CheckScrollableContainerConstraintsKt.checkScrollableContainerConstraints-K40F9xA(CheckScrollableContainerConstraints.kt:36)
                                                                                                    	at androidx.compose.foundation.ScrollingLayoutModifier.measure-3p2s80s(Scroll.kt:336)
I have tried to add
to the table layout modifier's chain in addition to adding the
, but this results in a new exception being thrown,
Copy code
java.lang.IllegalArgumentException: Can't represent a size of 2147483647 in Constraints
	at androidx.compose.ui.unit.Constraints$Companion.bitsNeedForSize(Constraints.kt:403)
	at androidx.compose.ui.unit.Constraints$Companion.createConstraints-Zbe2FdA$ui_unit_release(Constraints.kt:366)
	at androidx.compose.ui.unit.Constraints$Companion.fixedWidth-OenEA2s(Constraints.kt:327)
	at androidx.compose.foundation.layout.MinIntrinsicWidthModifier.calculateContentConstraints-l58MMJ0(Intrinsic.kt:118)
	at androidx.compose.foundation.layout.IntrinsicSizeModifier.measure-3p2s80s(Intrinsic.kt:252)
My table layout code:
Copy code
fun Table(
    contentRowsCount: Int,
    columnsCount: Int,
    modifier: Modifier = Modifier,
    headerRowContent: @Composable ((columnIndex: Int) -> Unit)? = null,
    cellContent: @Composable (rowIndex: Int, columnIndex: Int) -> Unit
) {
    val totalRowsCount = if (headerRowContent != null) contentRowsCount + 1 else contentRowsCount
        content = {
            repeat(totalRowsCount) { rowIndex ->
                repeat(columnsCount) { columnIndex ->
                    Cell(modifier = Modifier.cellLocation(rowIndex, columnIndex)) {
                        if (rowIndex == 0 && headerRowContent != null) {
                        } else {
                            cellContent(rowIndex, columnIndex)
        modifier = modifier
//            .width(IntrinsicSize.Max)
//            .height(IntrinsicSize.Max)
//            .wrapContentSize()
//            .verticalScroll(rememberScrollState())
//            .horizontalScroll(rememberScrollState())
    ) { measurables, constraints ->
        val rowsHeights: Map<Int, Int> = measurables
            .groupBy { it.rowIndex!! }
            .mapValues { (_, rowMeasurables) -> rowMeasurables.maxOf { it.minIntrinsicHeight(constraints.maxWidth) } }

        val columnsWidths: Map<Int, Int> = measurables
            .groupBy { it.columnIndex!! }
            .mapValues { (_, columnMeasurables) -> columnMeasurables.maxOf { it.minIntrinsicWidth(constraints.maxHeight) } }

        val placeables = Array<Array<Placeable?>>(totalRowsCount) { Array(columnsCount) { null } }
        measurables.forEach {
            val rowIndex = it.rowIndex!!
            val columnIndex = it.columnIndex!!
            placeables[rowIndex][columnIndex] = it.measure(constraints = Constraints.fixed(width = columnsWidths[columnIndex]!!, height = rowsHeights[rowIndex]!!))

        layout(constraints.maxWidth, constraints.maxHeight) {
            var x = 0
            var y = 0
            placeables.forEach { rowPlaceableColumns ->
                rowPlaceableColumns.forEach { placeable ->
                    placeable!!.placeRelative(x, y)
                    x += placeable.width
                x = 0
                y += rowPlaceableColumns.first()!!.height

fun Cell(modifier: Modifier = Modifier, cellContent: @Composable BoxScope.() -> Unit) {
        modifier = modifier.border(1.dp, Color.LightGray),
        content = cellContent,
        contentAlignment = Alignment.Center
Apparently, the problem was that I'm using the passed in table constraints which were always infinite to layout cells regardless of the actual area/size cells occupy. So, the solution was simple, I replaced:
Copy code
layout(constraints.maxWidth, constraints.maxHeight) {
with the total columns widths/rows heights:
Copy code
layout(columnsWidths.map { (_, width) -> width }.sum(), rowsHeights.map { (_, height) -> height }.sum()) {
and the scrolling worked as intended!