https://kotlinlang.org logo
#compose
Title
# compose
m

MR3Y

11/23/2023, 10:04 PM
I'm trying to position an element exactly like this red box by offsetting it upwards by half of its size, but the problem with my naive approach (Using
Modifier.layout {}
) is that it pushes the text beneath the row (red box + button) by the same offset amount leaving a weird vertical gap in between. Obviously offsetting the text upwards as well by the same amount doesn't solve the problem because it just moves that gap after the text. what I want to do is basically the same as the second picture but without that blank gap at the bottom.
My approach:
Copy code
@Composable
fun Sample() {
    Box(
        modifier = Modifier
            .wrapContentSize()
            .background(Color.Blue)
    ) {
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier
                .padding(top = 80.dp)
                .background(Color.White)
                .padding(horizontal = 16.dp)
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Box(
                    modifier = Modifier
                        .size(128.dp)
                        .layout { measurable, constraints ->
                            val placeable = measurable.measure(constraints)

                            val offsetY = -(placeable.height / 2)

                            layout(constraints.maxWidth, placeable.height) {
                                placeable.placeRelative(0, offsetY)
                            }
                        }
                        .background(Color.Red)
                )

                ElevatedButton(
                    onClick = { /*TODO*/ },
                    shape = RoundedCornerShape(16.dp),
                    colors = ButtonDefaults.elevatedButtonColors(
                        containerColor = MaterialTheme.colorScheme.primary,
                        contentColor = MaterialTheme.colorScheme.onPrimary
                    ),
                    modifier = Modifier.padding(vertical = 4.dp)
                ) {
                    Text(
                        text = "Button",
                        style = MaterialTheme.typography.labelLarge
                    )
                }
            }
            Text(
                text = "Text",
                color = Color.Black,
                fontSize = 32.sp
            )
        }
    }
}
I feel like there is an easy way, but I think I'm overcomplicating things.
g

Greg Steckman

11/23/2023, 10:30 PM
Not sure if this is what you are trying to do, but this closely replicates the layout on the right.
Copy code
Box {
        Box(modifier = Modifier.background(Color.Blue).height(64.dp + 16.dp).fillMaxWidth())
        Column(
            verticalArrangement = Arrangement.spacedBy(16.dp),
            modifier = Modifier
                .padding(top = 16.dp)
                .padding(horizontal = 16.dp)
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.Bottom
            ) {
                Box(
                    modifier = Modifier.size(128.dp).background(Color.Red)
                )

                ElevatedButton(
                    onClick = { /*TODO*/ },
                    shape = RoundedCornerShape(16.dp),
                    colors = ButtonDefaults.elevatedButtonColors(
                        containerColor = MaterialTheme.colorScheme.primary,
                        contentColor = MaterialTheme.colorScheme.onPrimary
                    ),
                    modifier = Modifier.padding(vertical = 4.dp)
                ) {
                    Text(
                        text = "Button",
                        style = MaterialTheme.typography.labelLarge
                    )
                }
            }
            Text(
                text = "Text",
                color = Color.Black,
                fontSize = 32.sp
            )
        }
    }
thank you color 1
m

MR3Y

11/23/2023, 10:50 PM
Unfortunately, it looks like the first picture which I don't want
It actually does work for this simple hardcoded values sample but the real usecase I want to use this for is a bit more involved which makes it the same as the first picture, I might take a deep look and see if i can tweak it to what i need
m

myanmarking

11/24/2023, 4:46 PM
create a Column with Box + Text, and use offset(-boxHeight / 2)
wouldn’t that work
m

MR3Y

11/24/2023, 5:49 PM
I've solved it already by tweaking the above code a bit, but using offset will not work(this is what I was actually using at the beginning), it will produce the same gap at the bottom as the second picture. This is probably due to the fact that
offset
doesn't affect layout.
g

Greg Steckman

11/24/2023, 5:57 PM
placeable.place(), or variants, position where the Composable is drawn but doesn't change the size provided to layout(). In the original example, the top of the red box was placed above the top of its container by using a negative y-axis coordinate. The parent Row has no way of knowing this - it lays out content according to the size of its children, which are the values provided in the call to layout().
m

myanmarking

11/24/2023, 5:59 PM
You have to offset both, obviously
m

MR3Y

11/24/2023, 6:14 PM
@Greg Steckman Yes, you're right. I forgot about that when I thought
Modifier.offset
is a possible solution.
@myanmarking When I offset both elements I'm just moving that gap to the next item laid vertically, So I end up with a weird gap at the bottom when I reach the end of this vertical column, Also, The screenshot above shows a minimal example, The real screen where I'm using this has actually many more elements, so offsetting every element I add to the column is not ideal.
3 Views