Subscribe to special discounts, free tools and open-source from Composables Subscribe now

LazyLayout

Common

Component in Compose Foundation

A layout that only composes and lays out currently needed items. Can be used to build efficient scrollable layouts.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.foundation:foundation:1.9.0-alpha04")
}

Overloads

@Deprecated("Please use overload with LazyLayoutMeasurePolicy", level = DeprecationLevel.HIDDEN)
@ExperimentalFoundationApi
@Composable
fun LazyLayout(
    itemProvider: () -> LazyLayoutItemProvider,
    modifier: Modifier = Modifier,
    prefetchState: LazyLayoutPrefetchState? = null,
    measurePolicy: LazyLayoutMeasureScope.(Constraints) -> MeasureResult,
)

Parameters

namedescription
itemProviderlambda producing an item provider containing all the needed info about the items which could be used to compose and measure items as part of [measurePolicy].
modifierto apply on the layout
prefetchStateallows to schedule items for prefetching
measurePolicyMeasure policy which allows to only compose and measure needed items.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyLayout(
    itemProvider: () -> LazyLayoutItemProvider,
    modifier: Modifier = Modifier,
    prefetchState: LazyLayoutPrefetchState? = null,
    measurePolicy: LazyLayoutMeasurePolicy,
)

Parameters

namedescription
itemProviderlambda producing an item provider containing all the needed info about the items which could be used to compose and measure items as part of [measurePolicy].
modifierto apply on the layout
prefetchStateallows to schedule items for prefetching
measurePolicyMeasure policy which allows to only compose and measure needed items.

Code Example

LazyLayoutSample

/** A simple Layout that will place items right to left with scrolling support. */
@Preview
@Composable
fun LazyLayoutSample() {
    val items = remember { (0..100).toList().map { it.toString() } }

    // Create an item provider
    val itemProvider = remember {
        {
            object : LazyLayoutItemProvider {
                override val itemCount: Int
                    get() = 100

                @Composable
                override fun Item(index: Int, key: Any) {
                    Box(
                        modifier =
                            Modifier.width(100.dp)
                                .height(100.dp)
                                .background(color = if (index % 2 == 0) Color.Red else Color.Green)
                    ) {
                        Text(text = items[index])
                    }
                }
            }
        }
    }

    LazyLayout(modifier = Modifier.size(500.dp), itemProvider = itemProvider) { constraints ->
        // plug the measure policy, this is how we create and layout items.
        val placeablesCache = mutableListOf<Pair<Placeable, Int>>()
        fun Placeable.mainAxisSize() = width
        fun Placeable.crossAxisSize() = height

        val childConstraints =
            Constraints(maxWidth = Constraints.Infinity, maxHeight = constraints.maxHeight)

        var currentItemIndex = 0
        var crossAxisSize = 0
        var mainAxisSize = 0

        // measure items until we either fill in the space or run out of items.
        while (mainAxisSize < constraints.maxWidth && currentItemIndex < items.size) {
            val itemPlaceables = compose(currentItemIndex).map { it.measure(childConstraints) }
            for (item in itemPlaceables) {
                // save placeable to be placed later.
                placeablesCache.add(item to mainAxisSize)

                mainAxisSize += item.mainAxisSize() // item size contributes to main axis size
                // cross axis size will the size of tallest/widest item
                crossAxisSize = maxOf(crossAxisSize, item.crossAxisSize())
            }
            currentItemIndex++
        }

        val layoutWidth = minOf(mainAxisSize, constraints.maxHeight)
        val layoutHeight = crossAxisSize

        layout(layoutWidth, layoutHeight) {
            // since this is a linear list all items are placed on the same cross-axis position
            for ((placeable, position) in placeablesCache) {
                placeable.place(position, 0)
            }
        }
    }
}
by @alexstyl