Jetpack Compose find parents width/length

Viewed 19053

I want to explicitely retrieve the value of the fillMaxSize(). Suppose i have:

    Box(Modifier
            .fillMaxSize()
            .background(Color.Yellow)) 
    {
var size = ?
Box(Modifier
            .size(someSize)
            .background(Color.Blue))
    {Text("Test")}

I want to change the size of my second Box multiple times (will probably stem from some viewmodel) and then reset it to maxSize.

How can i do that, I don't know any 'getMaxSize()'-method?

3 Answers

If you really need the raw size value, you can use the following code:

var size by remember { mutableStateOf(IntSize.Zero) }

Box(Modifier
    .fillMaxSize()
    .background(Color.Yellow)
    .onSizeChanged {
        size = it
    }
) {
    Box(
        Modifier
            .then(
                with(LocalDensity.current) {
                    Modifier.size(
                        width = size.width.toDp(),
                        height = size.height.toDp(),
                    )
                }
            )
            .background(Color.Blue)
    ) { Text("Test") }
}

But note, that is't not optimal in terms of performance: this view gets rendered two times. First time second box gets size zero, then the onSizeChanged block gets called, and then view gets rendered for the second time.

Be especially careful if using remember in top level views, because changing state will trigger full view stack re-render. Usually you want split your screen into views with states, so changing one view state only will re-render this view.

Also you can use BoxWithConstraints where you can get maxWidth/maxHeight inside the BoxWithConstraintsScope: it's much less code and a little better on performance.

BoxWithConstraints(
    Modifier
        .fillMaxSize()
        .background(Color.Yellow)
) {
    Box(
        Modifier
            .size(
                width = maxWidth,
                height = maxHeight,
            )
            .background(Color.Blue)
    ) { Text("Test") }
}

But usually if you wanna indicate size dependencies, using modifiers without direct knowing the size should be enough. It's more "Compose" way of writing code and more optimized one.

So if you wanna you second box be same size as the first one, just use .fillMaxSize() on it too. If you wanna it to be some part of the parent, you can add fraction param. To make second box size be half size of the first one, you do:

Box(
    Modifier
        .fillMaxSize(fraction = 0.5f)
) { Text("Test") }

If you wanna different parts for width/height:

Box(
    Modifier
        .fillMaxWidth(fraction = 0.3f)
        .fillMaxHeight(fraction = 0.7f)
) { Text("Test") }

In your first Box you can use the onGloballyPositioned modifier to get the size.
It is called with the final LayoutCoordinates of the Layout when the global position of the content may have changed.

Then use coordinates.size to get the size of the first Box.

var size by remember { mutableStateOf(Size.Zero)}

Box(
    Modifier
        .fillMaxSize()
        .background(Color.Yellow)
        .onGloballyPositioned { coordinates ->
            size = coordinates.size.toSize()
        }
        Box(){ /*....*/ }
)
Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                var parentSize by remember {
                    mutableStateOf(Size.Zero)
                }
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .aspectRatio(1f)
                        .background(Color.Green)
                ) {
                    Box(
                        modifier = Modifier
                            .size(100.dp)
                            .align(Alignment.Center)
                            .background(Color.Red)
                            .onGloballyPositioned {
                                //here u can access the parent layout coordinate size
                                parentSize = it.parentLayoutCoordinates?.size?.toSize()?: Size.Zero
                            }
                    ) {
                        Column(Modifier.fillMaxSize()) {
                            Text(text = "parent size  = $parentSize")
                        }
                    }
                }
            }

enter image description here

Related