1

I'm trying to get a child Box to extend outside parent Box when it's height is greater and it's aligned in a certain way. Because the child box is aligned bottom, I'd expect the Cyan line to not be visible when inside the parent box

enter image description here

But instead this happens

enter image description here

Child Box height resizes and prints out as being roughly 20.dp.

Is there a Modifier I can leverage to achieve this effect?

val density = LocalDensity.current

Box(modifier = Modifier
    .padding(top = 250.dp)
    .fillMaxWidth()
    .height(20.dp)
    .background(Color.Red)
    .clipToBounds()
) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(80.dp)
            .background(Color.Green)
            .align(Alignment.BottomCenter)
            .onSizeChanged {
                density.run {
                    Log.i("Box Height", it.height.toDp().toString())
                }
            }
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(5.dp)
                .background(Color.Cyan)
                .align(Alignment.TopCenter)
        )
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(5.dp)
                .background(Color.Yellow)
                .align(Alignment.BottomCenter)
        )
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .width(5.dp)
                .background(Color.Magenta)
                .align(Alignment.CenterStart)
        )
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .width(5.dp)
                .background(Color.Blue)
                .align(Alignment.CenterEnd)
        )
    }
}
Eric
  • 2,573
  • 1
  • 23
  • 19

2 Answers2

1

In Jetpack Compose you cannot increase parent size by increasing content size. With default Composables it's not even possible to measure Composables inside content out of range of min-max width/height of Constraints from parent.

Modifier.height(20.dp) returns minHeight =20.dp, max=20.dp so parent is always measured with 20.dp height.

However Modifier.wrapContentX(unBounded, align) or Modifier.requiredX() modifiers can change Constraints min-max range for content.

modifier = Modifier
    .fillMaxWidth()
    .wrapContentHeight(unbounded = true, align = Alignment.Bottom)
    .height(80.dp)
    .background(Color.Green)

will let Box with green background to be measured with 80.dp and by aligning bottom you will be able get expected result.

wrapContent definition is as

Allow the content to measure at its desired height without regard for the incoming measurement minimum height constraint, and, if unbounded is true, also without regard for the incoming measurement maximum height constraint. If the content's measured size is smaller than the minimum height constraint, align it within that minimum height space. If the content's measured size is larger than the maximum height constraint (only possible when unbounded is true), align over the maximum height space.

I replaced Box with green background with BoxWithConstraints so you can check out Constraints inside it, if you replace parent you can also constraints coming from it too.

enter image description here

@Preview
@Composable
fun Test() {

    Column(Modifier.fillMaxSize()) {

        BoxWithConstraints(
            modifier = Modifier
                .padding(top = 250.dp)
                .fillMaxWidth()
                .height(20.dp)
                .background(Color.Red)
                .clipToBounds()
        ) {

            println("Red Box minHeight: $minHeight, maxHeight: $maxHeight")

            BoxWithConstraints(
modifier = Modifier
    .fillMaxWidth()
    .wrapContentHeight(unbounded = true, align = Alignment.Bottom)
    .height(80.dp)
    .background(Color.Green)

            ) {

                println("Green Box minHeight: $minHeight, maxHeight: $maxHeight")

                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(5.dp)
                        .background(Color.Cyan)
                        .align(Alignment.TopCenter)
                )
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(5.dp)
                        .background(Color.Yellow)
                        .align(Alignment.BottomCenter)
                )
                Box(
                    modifier = Modifier
                        .fillMaxHeight()
                        .width(5.dp)
                        .background(Color.Magenta)
                        .align(Alignment.CenterStart)
                )
                Box(
                    modifier = Modifier
                        .fillMaxHeight()
                        .width(5.dp)
                        .background(Color.Blue)
                        .align(Alignment.CenterEnd)
                )
            }
        }
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • If you are interested in details about Constraints you can refer this answer. https://stackoverflow.com/a/73316247/5457853 – Thracian Aug 10 '23 at 20:01
1

I found a solution combining requiredHeight with offset y. It needs the offset too I think because Modifier.align(Alignment.BottomCenter) breaks after requiredHeight is added.

enter image description here

val parentHeight = 20.dp
val childHeight = 80.dp
val offsetY = (parentHeight - childHeight) / 2

Box(modifier = Modifier
    .padding(top = 250.dp)
    .fillMaxWidth()
    .height(parentHeight)
    .background(Color.Red)
    .clipToBounds()
) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .requiredHeight(childHeight)
            .offset(y = offsetY)
            .background(Color.Green)
            .align(Alignment.BottomCenter)
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(5.dp)
                .background(Color.Cyan)
                .align(Alignment.TopCenter)
        )
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(5.dp)
                .background(Color.Yellow)
                .align(Alignment.BottomCenter)
        )
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .width(5.dp)
                .background(Color.Magenta)
                .align(Alignment.CenterStart)
        )
        Box(
            modifier = Modifier
                .fillMaxHeight()
                .width(5.dp)
                .background(Color.Blue)
                .align(Alignment.CenterEnd)
        )
    }
}
Eric
  • 2,573
  • 1
  • 23
  • 19
  • 1
    When you use constraints that do not match parents constraints they are regarded as they are to be centered by parent. Parent tries to position it in center. You can also check out the link in comment section of my answer about Modifier.required – Thracian Aug 10 '23 at 20:05