Alek Falkowski
Alek Falkowski

Reputation: 3

How can I tell the Jetpack Compose to use one "instance" of composable function in different parts of the composable tree?

I use different composable tree structures for different screen sizes. When moving from one screen size to another, a recomposition occurs and the values saved using rememberSaveable, rememberLazyGridState, etc. are doubled for composable functions that have changed their position in the tree.

@Composable
fun BasicPageLayout(
    firstColumnContent: @Composable ((modifier: Modifier, contentMaxWidth: Int) -> Unit)? = null,
    secondColumnButtonIcon: ImageVector? = null,
    secondColumnButtonText: String? = null,
    secondColumnTitle: String? = null,
    secondColumnContent: @Composable ((modifier: Modifier) -> Unit)? = null,
) {
    val windowWidthClass: WindowWidthSizeClass = LocalWindowWidthClass.current
    val contentMaxWidth: Int = if (secondColumnContent == null) 1500 else 780

    when (windowWidthClass) {
        WindowWidthSizeClass.Expanded, // Expanded: Width > 840
        WindowWidthSizeClass.Medium -> { // Medium: 600 < width < 840
            Row(modifier = Modifier.fillMaxSize()) {
                firstColumnContent?.invoke(Modifier.weight(1f), contentMaxWidth)
                secondColumnContent?.invoke(Modifier.width(300.dp))
            }
        }

        else -> { // Compact: Width < 600
            Box(modifier = Modifier.fillMaxSize()) {
                firstColumnContent?.invoke(Modifier.fillMaxSize(), contentMaxWidth)
                secondColumnContent?.let {
                    EndSideDrawer(
                        secondColumnButtonIcon,
                        secondColumnButtonText,
                        secondColumnTitle,
                        secondColumnContent,
                    )
                }
            }
        }
    }
}

Due to the fact that for different screen sizes firstColumnContent is located in different branches of the condition, and secondColumnContent is additionally in different places in the tree, Jetpack Compose considers that these are different "instances" of these functions and duplicates the values saved for them using rememberSaveable, rememberLazyGridState, etc.

How can I tell the Jetpack Compose to use one "instance" of firstColumnContent/secondColumnContent in different branches and different parts of the composable tree?

I know that I can raise the state or move it to the viewModel, but I wouldn't want to do that for things like scroll position, etc.

I tried passing a static key to rememberSaveable, but it didn't work.

Upvotes: 0

Views: 200

Answers (1)

Jan B&#237;na
Jan B&#237;na

Reputation: 7278

There is a movableContentOf for that. From the docs:

Convert a lambda into one that moves the remembered state and nodes created in a previous call to the new location it is called.

There is even a sample that does pretty much exactly what you want:

@Composable
fun MovableContentColumnRowSample(content: @Composable () -> Unit, vertical: Boolean) {
    val movableContent = remember(content) { movableContentOf(content) }

    if (vertical) {
        Column {
            movableContent()
        }
    } else {
        Row {
            movableContent()
        }
    }
}

Upvotes: 0

Related Questions