How can I make the two buttons the same width when I use Jetpack Compose?

Issue

I run Code A and get Result A.

How can I make the two buttons the same width?

BTW, you know different string has different width, you can’t write a hard code such as Modifier.width(100.dp).

Code A

        Row(
            modifier =Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.Center
        ) {
            Button(
                modifier = Modifier,
                onClick = { }
            ) {
                Text("Short")
            }

            Button(
                modifier = Modifier.padding(start = 10.dp),
                onClick = { }
            ) {
                Text("This is a Long")
            }
        }

Result A

enter image description here

Solution

You can achieve setting width of long button to short one as in the answer described here with SubcomposeLayout.

What you need to do is subcompose your layout to check which element has longer width then subcompose your items again with this width as minimum constraint.

@Composable
private fun SubcomposeRow(
    modifier: Modifier = Modifier,
    paddingBetween: Dp = 0.dp,
    content: @Composable () -> Unit = {},
) {
    val density = LocalDensity.current

    SubcomposeLayout(modifier = modifier) { constraints ->

        var subcomposeIndex = 0

        val spaceBetweenButtons = with(density) {
            paddingBetween.roundToPx()
        }

        var placeables: List<Placeable> = subcompose(subcomposeIndex++, content)
            .map {
                it.measure(constraints)
            }

        var maxWidth = 0
        var maxHeight = 0
        var layoutWidth = 0

        placeables.forEach { placeable: Placeable ->
            maxWidth = placeable.width.coerceAtLeast(maxWidth)
                .coerceAtMost(((constraints.maxWidth - spaceBetweenButtons) / 2))
            maxHeight = placeable.height.coerceAtLeast(maxHeight)
        }


        layoutWidth = maxWidth

        // Remeasure every element using width of longest item using it as min width
        // Our max width is half of the remaining area after we subtract space between buttons
        // and we constraint its maximum width to half width minus space between
        if (placeables.isNotEmpty() && placeables.size > 1) {
            placeables = subcompose(subcomposeIndex, content).map { measurable: Measurable ->
                measurable.measure(
                    constraints.copy(
                        minWidth = maxWidth,
                        maxWidth = ((constraints.maxWidth - spaceBetweenButtons) / 2)
                            .coerceAtLeast(maxWidth)
                    )
                )
            }

            layoutWidth = (placeables.sumOf { it.width } + spaceBetweenButtons)
                .coerceAtMost(constraints.maxWidth)

            maxHeight = placeables.maxOf { it.height }
        }

        layout(layoutWidth, maxHeight) {
            var xPos = 0
            placeables.forEach { placeable: Placeable ->
                placeable.placeRelative(xPos, 0)
                xPos += placeable.width + spaceBetweenButtons
            }
        }
    }
}

you can change this layouWidth with constraints.maxWidth if you want it to occupy available space.

Then, instead of setting them from beginning of Composable you need to have your algorithm for laying them out at 0 y position and x position from beginning of the Composable if you want to have different spacings.

placeable.placeRelative(xPos, 0)

Usage

@Composable
private fun Sample() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp)
    ) {

        Spacer(modifier = Modifier.height(20.dp))

        SubcomposeRow(
            modifier = Modifier.background(Color.LightGray).border(3.dp, Color.Green),
            paddingBetween = 20.dp
        ) {
            Button(
                modifier = Modifier,
                onClick = { }
            ) {
                Text("Short")
            }

            Button(
                modifier = Modifier,
                onClick = { }
            ) {
                Text("This is a Long")
            }
        }

        Spacer(modifier = Modifier.height(20.dp))

        SubcomposeRow(
            modifier = Modifier.background(Color.LightGray).border(3.dp, Color.Green),
            paddingBetween = 20.dp
        ) {
            Button(
                modifier = Modifier,
                onClick = { }
            ) {
                Text("Short")
            }

            Button(
                modifier = Modifier,
                onClick = { }
            ) {
                Text("This is a Long a button with text")
            }
        }
    }
}

Result

enter image description here

Answered By – Thracian

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published