How can I measure a composable and update the Padd...
# compose
z
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
Look at
Scaffold
implementation. It uses
SubcomposeLayout
for this.
z
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
Glad it worked out for you ๐Ÿ™Œ
๐Ÿ™๐Ÿฝ 1
c
What'd you end up doing Zoltan? I'm intrigued by your question and never used subcomposeLayout (but I have used onSizeChanged)
z
@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
It looks like the multiple measures exception is called in function called
remeasure
๐Ÿ˜„
(starred btw โญ )
๐ŸŒŸ 1