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

Zoltan Demant

12/13/2021, 9:43 AM
How can I measure a composable and update the PaddingValues of another composable without using
Modifier.onSizeChanged
(in other words, without causing an additional recomposition for the entire screen)? Is this even possible? 🤷🏽‍♂️
f

Filip Wiesner

12/13/2021, 9:55 AM
Look at
Scaffold
implementation. It uses
SubcomposeLayout
for this.
z

Zoltan Demant

12/13/2021, 9:55 AM
Oh, good idea, thank you!
👌 1
Holy shit, it actually works. Having never used SubcomposeLayout before, to doing all of this within 15 minutes just speaks to how awesome compose is 😮
👏 1
f

Filip Wiesner

12/13/2021, 10:32 AM
Glad it worked out for you 🙌
🙏🏽 1
c

Colton Idle

12/13/2021, 4:18 PM
What'd you end up doing Zoltan? I'm intrigued by your question and never used subcomposeLayout (but I have used onSizeChanged)
z

Zoltan Demant

12/13/2021, 6:36 PM
@Colton Idle Ill drop the code below! I just calculate the fab-size and add it onto the paddingValues; same thing I did with the onSizeChanged modifier earlier - but this way the content is just laid out once, with the right padding right away. It made a clear difference in terms of performance, animations are super-smooth now! 🦜
Copy code
SubcomposeLayout { constraints ->
    val maxWidth = constraints.maxWidth
    val maxHeight = constraints.maxHeight

    val looseConstraints = constraints.copy(
        minWidth = 0,
        minHeight = 0
    )

    layout(
        width = maxWidth,
        height = maxHeight,
        placementBlock = {
            val actionPlaceables = subcompose(
                slotId = Action,
                content = action
            ).fastMap { measurable ->
                measurable.measure(looseConstraints)
            }

            val tallestAction = actionPlaceables.fastMaxBy(Placeable::height)
            val actionHeight = tallestAction?.height ?: 0

            val bodyPlaceables = subcompose(
                slotId = Body,
                content = {
                    val innerPadding = remember(
                        key1 = originalPadding,
                        key2 = actionHeight
                    ) {
                        val extraBottomPadding = if (actionHeight > 0) {
                            actionHeight.toDp() + KeylineSmall * 2
                        } else {
                            Zero
                        }

                        PaddingValues(
                            start = originalPadding.calculateStartPadding(layoutDirection),
                            top = originalPadding.calculateTopPadding(),
                            end = originalPadding.calculateEndPadding(layoutDirection),
                            bottom = originalPadding.calculateBottomPadding() + extraBottomPadding
                        )
                    }

                    content(innerPadding)
                }
            ).fastMap { measurable ->
                measurable.measure(looseConstraints)
            }

            bodyPlaceables.fastForEach { placeable ->
                placeable.place(
                    x = 0,
                    y = 0
                )
            }

            actionPlaceables.fastForEach { placeable ->
                placeable.place(
                    x = 0,
                    y = maxHeight - actionHeight
                )
            }
        }
    )
}
K 1
Cautionary tale, theres apparently a bug in compose that got aggravated by me doing this. It still happens using the regular
Scaffold
, but more so with my own implementation for some reason that I dont understand.
f

Filip Wiesner

12/14/2021, 12:49 PM
It looks like the multiple measures exception is called in function called
remeasure
😄
(starred btw )
🌟 1
60 Views