Martin
Martin

Reputation: 539

Jetpack Compose find parent's width/length

I want to explicitly 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?

Upvotes: 37

Views: 56413

Answers (4)

Geraldo Neto
Geraldo Neto

Reputation: 4030

Take a look at BoxWithConstraints :

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                //...
            }
        } else {
            Row{
              //...
            }
        }
    }
}

It gives you a maxWidth and a maxHeight to create your logic. More info here on this Android Docs.

Hope it helps!

Upvotes: 5

Phil Dukhov
Phil Dukhov

Reputation: 87605

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 it's 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") }

Upvotes: 63

Abhijith mogaveera
Abhijith mogaveera

Reputation: 1284

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

Upvotes: 1

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363429

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(){ /*....*/ }
)

Upvotes: 25

Related Questions