Composables UI is out: our new component library for Compose Multiplatform ->
Interface

TransformingLazyColumnFirstLayoutItemProvider

Provides the first item to layout for TransformingLazyColumn.

TransformingLazyColumnFirstLayoutItemProviderSample

@Preview
@Composable
fun TransformingLazyColumnFirstLayoutItemProviderSample() {
    val state = rememberTransformingLazyColumnState()
    val transformationSpec = rememberTransformationSpec()
    var expandedItemIndex by remember { mutableIntStateOf(-1) }
    // This sample demonstrates how to use the provider API to control the direction of content
    // shifting. By default, TransformingLazyColumn uses the center item as the layout reference.
    // This means that if an item above the center expands, it pushes content upwards;
    // if below, it pushes downwards.
    //
    // Here, we fix the Bottom/End edge of the clicked item regardless of its position on screen,
    // so that when its animated content appears, the card predictably expands *upwards* every time.
    val upwardExpandingItemProvider =
        remember(state) {
            TransformingLazyColumnFirstLayoutItemProvider { centerItem ->
                val item = expandedItemIndex
                // Yield to the standard layout behavior during active scrolls.
                // This avoids custom layout overhead and ensures the [TransformingLazyColumn]
                // tracks the user's scroll gesture using its default center layout reference.
                if (item == -1 || state.isScrollInProgress) {
                    return@TransformingLazyColumnFirstLayoutItemProvider centerItem
                }
                // Look up the item's offset from state.layoutInfo (which holds the details
                // from the previous measure pass) to maintain its visual position in the current
                // pass.
                state.layoutInfo.visibleItems
                    .fastFirstOrNull { visibleItem -> visibleItem.index == item }
                    ?.let { visibleItem ->
                        TransformingLazyColumnFirstLayoutItemProvider.ItemInfo(
                            key = visibleItem.key,
                            index = visibleItem.index,
                            // Pin the bottom edge of the item
                            itemEdge = ItemEdge.End,
                            // Calculate the exact bottom offset from the previous pass
                            offset = visibleItem.offset + visibleItem.transformedHeight,
                        )
                    } ?: centerItem
            }
        }
    TransformingLazyColumn(
        state = state,
        contentPadding = PaddingValues(horizontal = 20.dp),
        firstLayoutItemProvider = upwardExpandingItemProvider,
    ) {
        items(count = 10, key = { it }) { cardIndex ->
            val isExpanded = expandedItemIndex == cardIndex
            TitleCard(
                onClick = { expandedItemIndex = cardIndex },
                modifier =
                    Modifier.minimumVerticalContentPadding(
                            CardDefaults.minimumVerticalListContentPadding
                        )
                        .fillMaxWidth()
                        .transformedHeight(this, transformationSpec),
                transformation = SurfaceTransformation(transformationSpec),
                title = { Text("Card $cardIndex") },
                subtitle = {
                    AnimatedVisibility(isExpanded) { Text("Expanded content is available here") }
                },
                content = { Text("Tap to expand") },
            )
        }
    }
}

Last updated: