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

Stylianos Gakis

03/07/2022, 5:01 PM
If I am doing a
Layout {}
which may accept arbitrary composable slots, and I want to be able to add a modifier to them to add a
layoutId
how would I go around doing that?
Currently have something like this
Copy code
@Composable
fun MyLayout(
  slot1: @Composable (Modifier),
  slot2: @Composable (Modifier),
) {
  Layout(
    content = {
      slot1(Modifier.layoutId("slot1"))
      slot2(Modifier.layoutId("slot2"))
    },
  ) { measurables, constraints ->
    val slot1Placeable = measurables.first { it.layoutId == "slot1" }.measure(constraints)
    val slot2Placeable = measurables.first { it.layoutId == "slot2" }.measure(constraints)
    // etc.
  }
}
But this makes it awkward to use on the call-site, since I have to make it a lambda that receives a
modifier
and I can’t just call other composables easier, I have to make it look like
{ modifier -> WhateverComposable(modifier) }
One thing I can see doing is replacing
slot1(Modifier.layoutId("slot1"))
with
Copy code
Box(Modifier.layoutId("slot1")) { slot1() }
Is this like weird? Feels a bit like it. Does it only work since Box is inline? Is there maybe some better approach 🤔
a

Adam Powell

03/07/2022, 6:59 PM
Not weird; the wrapper approach for separate content slots like this is pretty standard/recommended
you'll likely want to pass along the `BoxScope`/`RowScope`/`ColumnScope` receiver to the slots as appropriate for the container you're emitting them in
an alternative would be to define your own layout scope and let the developer tag specific child elements using your own modifier provided in that scope instead of using separate slots
you could define that scoped modifier as just using
layoutId
as the implementation detail or go all the way to defining your own
ParentData
for it
s

Stylianos Gakis

03/07/2022, 8:53 PM
Right of course, if they were used that way I could also make them know that they were inside a Box scope etc, that’s very interesting indeed. In my case I am making a very specific layout so I am afraid I wouldn’t be getting anything out of it, especially with the way I am kinda hardcoding in the layout having one of the things centered and the other at the top and centered, but thanks a lot for those tips, I can keep them in mind when doing some other kind of more reusable layout.
With that said, you completely lost me at the last two messages. I think I am missing a lot of context to make sense of what you’re suggesting there. I’ve seen
ParentData
being mentioned once before again and I again didn’t really get it. Do you happen to have some sort of application of that on https://cs.android.com/ maybe? Something that preferably is build to showcase this functionality instead of where it’s actually used, because it the usually becomes quite hard to understand some things when too many concepts are mixed together.
ParentData is to Compose what LayoutParams are to Views; it's an object that can be associated with a child but only interpreted by the parent
so this is how things like Modifier.align in a BoxScope is implemented; same for things like weight in row/column. It's a parameter that a specific parent knows how to unpack and use during measurement and layout
these are just to make it a bit more readable
if you chose to implement your own layout scope on top of the existing
layoutId
modifier you could do that here as a shortcut, provided you have no reason to use a layoutId for anything else in the same parent
so instead of defining your own ParentDataModifier you could just define your own scoped modifier function as
= layoutId("foo")
and then have your measure/layout read the layoutId instead