One Off the Slack: The Phases of Compose UI Rendering

Alejandro Carbajo asked:

is there a way to know the maximum height and maximum width of a Column or (Row)? I just know about BoxWithConstraints

I want to animate an object from the beginning to the end of the column, for example

Alejandro later clarified that the objective is for the container to be a fixed size, with the content animating within that space.

Google’s Adam Powell offered:

Modifier.weight(1f)
  .wrapContentWidth(align = Alignment { size, space, layoutDirection -> /* TODO: return a value based on the animation */ })

Alejandro promised to try it, and commented:

sometimes I feel lost because it seems that there are many ways to do the same, but each has its nuances

This led to an epic series of posts from Adam, talking about how rendering really works:

Under the hood there are only a few major concepts, but lots of purpose-built helpers on top of those for different situations. To give the advice above I started from which of those low-level concepts should be responsible and then looked for an existing helper that does that thing.

Compose’s major output phases are composition, layout, and drawing.

Composition is for making structural changes to the UI; we didn’t need that here, we just wanted to move something around.

Drawing is just for painting whatever has already been positioned, not really for positioning those things

which leaves layout as the step to explore

That leaves two main tools: the Layout composable for coordinating multiple elements together and the Modifier.layout {} modifier for altering the sizing and positioning of single elements in isolation. We only needed to manipulate one thing, so the modifier is the easier way to work with it. The Layout composable always has to deal with the possibility of zero or more than one child element, which always adds some complexity. Better to skip that when you can.

The behavior we wanted could be done with Modifier.layout; it gets the min/max sizes requested, and we can measure the child in any way we need. Then we can report a size for the modified element and position the measured child within that space.

Modifier.wrapContentSize already implements most of that for us and by writing a custom Alignment we can worry only about that last part - the positioning within the space occupied.

and then we can simplify even further to Modifier.wrapContentWidth or Height if all we need is either vertical or horizontal alignment, but not both.


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!