Stylianos Gakis
11/09/2024, 2:12 AM{}
for one of the slots, then trying to measure it will just not find that item at all.
So with this
fun CustomLayout(
firstSlot: @Composable () -> Unit,
secondSlot: @Composable () -> Unit,
) {
Layout(
content = {
firstSlot()
secondSlot()
},
) { measurable, constraints ->
val firstPlaceable = measurable[0].measure(constraints)
val secondPlaceable = measurable[1].measure(
...
}
}
So if someone passes firstSlot = {}
the first measurable would in fact be whatever secondSlot had inside, and then when trying to access the second measurable this crashes.
What is my best bet here to make sure I can avoid that?Stylianos Gakis
11/09/2024, 2:13 AMfun CustomLayout(
firstSlot: @Composable () -> Unit,
secondSlot: @Composable () -> Unit,
) {
Layout(
content = {
Box { firstSlot() }
Box { secondSlot() }
},
) { measurable, constraints ->
val firstPlaceable = measurable[0].measure(constraints)
val secondPlaceable = measurable[1].measure(
...
}
}
But this affects the way that the two composables are in fact laid out in the end. I think they lose part of the information of their incoming constraintsStylianos Gakis
11/09/2024, 2:14 AMfun CustomLayout(
firstSlot: @Composable () -> Unit,
secondSlot: @Composable () -> Unit,
) {
Layout(
content = {
Box(propagateMinConstraints = true) { firstSlot() }
Box(propagateMinConstraints = true) { secondSlot() }
},
) { measurable, constraints ->
val firstPlaceable = measurable[0].measure(constraints)
val secondPlaceable = measurable[1].measure(
...
}
}
And I think this has worked well so far. But is there anything else I could be missing here?
If anyone else has tried to do this before and has done something differently I would be happy to hear about it!Albert Chang
11/09/2024, 2:19 AMLayout
with contents
parameter which allows you to pass in multiple composable lambdas.Stylianos Gakis
11/09/2024, 2:28 AM{}
though right?
Doing a naive impl like this
Layout(
modifier = modifier,
contents = listOf(
{ firstSlot() },
{ secondSlot() },
),
) { measurables, constraints ->
val firstPlaceable = measurables[0][0].measure(constraints)
val secondPlaceable = measurables[1][0].measure(
}
It will simply find nothing at [0]
which would be inside the list itself. I'd still then need to do measurables[0].getOrNull(0)
and measurables[1].getOrNull(0)
and then fallback in case of null to whatever makes sense in my scenarioStylianos Gakis
11/09/2024, 2:29 AM{}
then it just measures as a placeable with 0 width and height.
Which I could I suppose default to (to the 0 width and height that is) in case of null using the list of measurables approach, right?Albert Chang
11/09/2024, 2:54 AMBox
, using that is perfectly fine. I'm not sure what you are looking for besides.Louis Pullen-Freilich [G]
11/09/2024, 4:30 AMefemoney
11/09/2024, 11:39 AMYou can use Modifier.layoutId …Wish compose provided better apis than this for this & related use cases 🤦🏾♂️
Stylianos Gakis
11/09/2024, 11:44 AMfirstSlot = {
Column { // or Box, or Row, or whatever they want really
One()
Two()
}
}
Or this should be allowed too
firstSlot = {}
But I never want them to do
firstSlot = {
One()
Two()
}
I suppose there is no way I could expose that rule through the function signature right?
Regarding adding a layoutId, I have done that in the past and it is quite convenient. In this particular case however since I do always wrap them in a box myself there's no way doing [0] and [1] will fail on my measurables list as far as I understand. This would be good enough for the needs of this layout I think.
So my problem with adding a wrapping box initially was that it was then altering the constraints that were passed to the slot content itself.
In my measure logic for this particular layout I wanted secondSlot to always be at least as tall and as wide as the firstSlot.
I was doing that by doing:
val firstPlaceable = measurable[0].measure(constraints)
val secondPlaceable = measurable[1].measure(
constraints.copy(
minWidth = firstPlaceable.width.coerceAtLeast(constraints.minWidth),
minHeight = firstPlaceable.height.coerceAtLeast(constraints.minHeight),
),
)
Then the second box was measured exactly as I wanted it to, so that's good. However then on the call site, I was expecting whatever I was putting inside secondSlot to also have those minWidth and minHeight constraints. Before I realized I needed the box to guard against the firstSlot = {}
scenario that worked well, but when I added the box, the way that the box seems to work is that it takes the constraints, respects them, but then for its children it by default gives them a min width/height of 0
So adding the Box + propagateMinConstraints does seem to tick all of my boxes here.
• I guard against empty lambda cases.
• I know for sure I always have to measure two things, and they are always in positions [0] and [1]
• The min size of the second slot is always at least as big as the first slot.efemoney
11/09/2024, 11:45 AMcontents
and you cannot wrap in a box! or else you wont have control.
If not … theres nothing more you can do for this other than wrap in a box that propagates constraints and give it a layout ID.efemoney
11/09/2024, 11:47 AM…it is quite convenientBeg to differ, literally one of the most inconvenient APIs in compose for anything but the most trivial custom layout use cases.
Stylianos Gakis
11/09/2024, 12:01 PMZakir Sheikh
12/03/2024, 5:47 PM