androidx.compose.foundation.lazy.staggeredgrid

Interfaces

LazyStaggeredGridItemInfo

Information about layout state of individual item in lazy staggered grid.

Cmn
LazyStaggeredGridItemScope

Receiver scope for itemContent in LazyStaggeredGridScope.item

Cmn
LazyStaggeredGridLayoutInfo

Information about layout state of lazy staggered grids.

Cmn
LazyStaggeredGridScope

Receiver scope for LazyVerticalStaggeredGrid and LazyHorizontalStaggeredGrid

Cmn
StaggeredGridCells

This class describes the count and the sizes of columns in vertical staggered grids, or rows in horizontal staggered grids.

Cmn

Classes

LazyStaggeredGridState

Hoisted state object controlling LazyVerticalStaggeredGrid or LazyHorizontalStaggeredGrid.

Cmn
StaggeredGridCells.Adaptive

Defines a grid with as many rows or columns as possible on the condition that every cell has at least minSize space and all extra space distributed evenly.

Cmn
StaggeredGridCells.Fixed

Defines a grid with fixed number of rows or columns.

Cmn
StaggeredGridCells.FixedSize

Defines a grid with as many rows or columns as possible on the condition that every cell takes exactly size space.

Cmn
StaggeredGridItemSpan

Span defines a number of lanes (columns in vertical grid/rows in horizontal grid) for staggered grid items.

Cmn

Top-level functions summary

Unit
@Composable
LazyHorizontalStaggeredGrid(
    rows: StaggeredGridCells,
    modifier: Modifier,
    state: LazyStaggeredGridState,
    contentPadding: PaddingValues,
    reverseLayout: Boolean,
    verticalArrangement: Arrangement.Vertical,
    horizontalItemSpacing: Dp,
    flingBehavior: FlingBehavior,
    userScrollEnabled: Boolean,
    overscrollEffect: OverscrollEffect?,
    content: LazyStaggeredGridScope.() -> Unit
)

Horizontal staggered grid layout that composes and lays out only items currently visible on screen.

Cmn
LazyLayoutScrollScope
LazyLayoutScrollScope(
    state: LazyStaggeredGridState,
    scrollScope: ScrollScope
)

An implementation of LazyLayoutScrollScope that can be used with LazyStaggeredGrids.

Cmn
Unit
@Composable
LazyVerticalStaggeredGrid(
    columns: StaggeredGridCells,
    modifier: Modifier,
    state: LazyStaggeredGridState,
    contentPadding: PaddingValues,
    reverseLayout: Boolean,
    verticalItemSpacing: Dp,
    horizontalArrangement: Arrangement.Horizontal,
    flingBehavior: FlingBehavior,
    userScrollEnabled: Boolean,
    overscrollEffect: OverscrollEffect?,
    content: LazyStaggeredGridScope.() -> Unit
)

Vertical staggered grid layout that composes and lays out only items currently visible on screen.

Cmn
LazyStaggeredGridState
@Composable
rememberLazyStaggeredGridState(
    initialFirstVisibleItemIndex: Int,
    initialFirstVisibleItemScrollOffset: Int
)

Creates a LazyStaggeredGridState that is remembered across composition.

Cmn

Extension functions summary

inline Unit
<T : Any?> LazyStaggeredGridScope.items(
    items: Array<T>,
    noinline key: ((item) -> Any)?,
    crossinline contentType: (item) -> Any,
    noinline span: ((item) -> StaggeredGridItemSpan)?,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit
)

Add an array of items to the staggered grid.

Cmn
inline Unit
<T : Any?> LazyStaggeredGridScope.items(
    items: List<T>,
    noinline key: ((item) -> Any)?,
    crossinline contentType: (item) -> Any,
    noinline span: ((item) -> StaggeredGridItemSpan)?,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit
)

Add a list of items to the staggered grid.

Cmn
inline Unit
<T : Any?> LazyStaggeredGridScope.itemsIndexed(
    items: Array<T>,
    noinline key: ((index: Int, item) -> Any)?,
    crossinline contentType: (index: Int, item) -> Any,
    noinline span: ((index: Int, item) -> StaggeredGridItemSpan)?,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit
)

Add an array of items with index-aware content to the staggered grid.

Cmn
inline Unit
<T : Any?> LazyStaggeredGridScope.itemsIndexed(
    items: List<T>,
    noinline key: ((index: Int, item) -> Any)?,
    crossinline contentType: (index: Int, item) -> Any,
    noinline span: ((index: Int, item) -> StaggeredGridItemSpan)?,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit
)

Add a list of items with index-aware content to the staggered grid.

Cmn

Top-level functions

LazyHorizontalStaggeredGrid

@Composable
fun LazyHorizontalStaggeredGrid(
    rows: StaggeredGridCells,
    modifier: Modifier = Modifier,
    state: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
    contentPadding: PaddingValues = PaddingValues(0.dp),
    reverseLayout: Boolean = false,
    verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(0.dp),
    horizontalItemSpacing: Dp = 0.dp,
    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
    userScrollEnabled: Boolean = true,
    overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(),
    content: LazyStaggeredGridScope.() -> Unit
): Unit

Horizontal staggered grid layout that composes and lays out only items currently visible on screen.

Sample:

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyHorizontalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.itemsIndexed
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

val itemsList = (0..5).toList()
val itemsIndexedList = listOf("A", "B", "C")

val itemModifier = Modifier.border(1.dp, Color.Blue).padding(16.dp).wrapContentSize()

LazyHorizontalStaggeredGrid(rows = StaggeredGridCells.Fixed(3)) {
    items(itemsList) { Text("Item is $it", itemModifier) }
    item { Text("Single item", itemModifier) }
    itemsIndexed(itemsIndexedList) { index, item ->
        Text("Item at index $index is $item", itemModifier)
    }
}

Sample with custom item spans:

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyHorizontalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

val sections = (0 until 25).toList().chunked(5)
LazyHorizontalStaggeredGrid(
    rows = StaggeredGridCells.Fixed(3),
    verticalArrangement = Arrangement.spacedBy(16.dp),
    horizontalItemSpacing = 16.dp
) {
    sections.forEachIndexed { index, items ->
        item(span = StaggeredGridItemSpan.FullLine) {
            Text(
                "This is section $index",
                Modifier.border(1.dp, Color.Gray).padding(16.dp).wrapContentSize()
            )
        }
        items(
            items,
            // not required as it is the default
            span = { StaggeredGridItemSpan.SingleLane }
        ) {
            Text("Item $it", Modifier.border(1.dp, Color.Blue).width(80.dp).wrapContentSize())
        }
    }
}
Parameters
rows: StaggeredGridCells

description of the size and number of staggered grid columns.

modifier: Modifier = Modifier

modifier to apply to the layout.

state: LazyStaggeredGridState = rememberLazyStaggeredGridState()

state object that can be used to control and observe staggered grid state.

contentPadding: PaddingValues = PaddingValues(0.dp)

padding around the content.

reverseLayout: Boolean = false

reverse the direction of scrolling and layout. When true, items are laid out in the reverse order and LazyStaggeredGridState.firstVisibleItemIndex == 0 means that grid is scrolled to the end.

verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(0.dp)

arrangement specifying vertical spacing between items. The item arrangement specifics are ignored for now.

horizontalItemSpacing: Dp = 0.dp

horizontal spacing between items.

flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior()

logic responsible for handling fling.

userScrollEnabled: Boolean = true

whether scroll with gestures or accessibility actions are allowed. It is still possible to scroll programmatically through state when userScrollEnabled is set to false

overscrollEffect: OverscrollEffect? = rememberOverscrollEffect()

the OverscrollEffect that will be used to render overscroll for this layout. Note that the OverscrollEffect.node will be applied internally as well - you do not need to use Modifier.overscroll separately.

content: LazyStaggeredGridScope.() -> Unit

a lambda describing the staggered grid content. Inside this block you can use LazyStaggeredGridScope.items to present list of items or LazyStaggeredGridScope.item for a single one.

LazyLayoutScrollScope

fun LazyLayoutScrollScope(
    state: LazyStaggeredGridState,
    scrollScope: ScrollScope
): LazyLayoutScrollScope

An implementation of LazyLayoutScrollScope that can be used with LazyStaggeredGrids.

import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.layout.LazyLayoutScrollScope
import androidx.compose.foundation.lazy.staggeredgrid.LazyHorizontalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.LazyLayoutScrollScope
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

suspend fun LazyStaggeredGridState.customScroll(
    block: suspend LazyLayoutScrollScope.() -> Unit
) = scroll { block.invoke(LazyLayoutScrollScope(this@customScroll, this)) }

val itemsList = (0..100).toList()
val state = rememberLazyStaggeredGridState()
val scope = rememberCoroutineScope()

Column(Modifier.verticalScroll(rememberScrollState())) {
    Button(
        onClick = {
            scope.launch {
                state.customScroll {
                    snapToItem(40, 0) // teleport to item 40
                    val distance = calculateDistanceTo(50).toFloat()
                    var previousValue = 0f
                    androidx.compose.animation.core.animate(
                        0f,
                        distance,
                        animationSpec = tween(5_000)
                    ) { currentValue, _ ->
                        previousValue += scrollBy(currentValue - previousValue)
                    }
                }
            }
        }
    ) {
        Text("Scroll To Item 50")
    }
    LazyHorizontalStaggeredGrid(
        state = state,
        rows = StaggeredGridCells.Fixed(3),
        modifier = Modifier.height(600.dp).fillMaxWidth()
    ) {
        items(itemsList) {
            Box(Modifier.padding(2.dp).background(Color.Red).size(45.dp)) {
                Text(it.toString())
            }
        }
    }
}
Parameters
state: LazyStaggeredGridState

The LazyStaggeredGridState associated with the layout where this custom scroll should be performed.

scrollScope: ScrollScope

The base ScrollScope where the scroll session was created.

LazyVerticalStaggeredGrid

@Composable
fun LazyVerticalStaggeredGrid(
    columns: StaggeredGridCells,
    modifier: Modifier = Modifier,
    state: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
    contentPadding: PaddingValues = PaddingValues(0.dp),
    reverseLayout: Boolean = false,
    verticalItemSpacing: Dp = 0.dp,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(0.dp),
    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
    userScrollEnabled: Boolean = true,
    overscrollEffect: OverscrollEffect? = rememberOverscrollEffect(),
    content: LazyStaggeredGridScope.() -> Unit
): Unit

Vertical staggered grid layout that composes and lays out only items currently visible on screen.

Sample:

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.lazy.staggeredgrid.itemsIndexed
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

val itemsList = (0..5).toList()
val itemsIndexedList = listOf("A", "B", "C")

val itemModifier = Modifier.border(1.dp, Color.Blue).wrapContentSize()

LazyVerticalStaggeredGrid(columns = StaggeredGridCells.Fixed(3)) {
    items(itemsList) { Text("Item is $it", itemModifier.height(80.dp)) }
    item { Text("Single item", itemModifier.height(100.dp)) }
    itemsIndexed(itemsIndexedList) { index, item ->
        Text("Item at index $index is $item", itemModifier.height(60.dp))
    }
}

Sample with custom item spans:

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

val sections = (0 until 25).toList().chunked(5)
LazyVerticalStaggeredGrid(
    columns = StaggeredGridCells.Fixed(3),
    horizontalArrangement = Arrangement.spacedBy(16.dp),
    verticalItemSpacing = 16.dp
) {
    sections.forEachIndexed { index, items ->
        item(span = StaggeredGridItemSpan.FullLine) {
            Text(
                "This is section $index",
                Modifier.border(1.dp, Color.Gray).height(80.dp).wrapContentSize()
            )
        }
        items(
            items,
            // not required as it is the default
            span = { StaggeredGridItemSpan.SingleLane }
        ) {
            Text("Item $it", Modifier.border(1.dp, Color.Blue).height(80.dp).wrapContentSize())
        }
    }
}
Parameters
columns: StaggeredGridCells

description of the size and number of staggered grid columns.

modifier: Modifier = Modifier

modifier to apply to the layout.

state: LazyStaggeredGridState = rememberLazyStaggeredGridState()

state object that can be used to control and observe staggered grid state.

contentPadding: PaddingValues = PaddingValues(0.dp)

padding around the content.

reverseLayout: Boolean = false

reverse the direction of scrolling and layout. When true, items are laid out in the reverse order and LazyStaggeredGridState.firstVisibleItemIndex == 0 means that grid is scrolled to the bottom.

verticalItemSpacing: Dp = 0.dp

vertical spacing between items.

horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(0.dp)

arrangement specifying horizontal spacing between items. The item arrangement specifics are ignored for now.

flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior()

logic responsible for handling fling.

userScrollEnabled: Boolean = true

whether scroll with gestures or accessibility actions are allowed. It is still possible to scroll programmatically through state when userScrollEnabled is set to false

overscrollEffect: OverscrollEffect? = rememberOverscrollEffect()

the OverscrollEffect that will be used to render overscroll for this layout. Note that the OverscrollEffect.node will be applied internally as well - you do not need to use Modifier.overscroll separately.

content: LazyStaggeredGridScope.() -> Unit

a lambda describing the staggered grid content. Inside this block you can use LazyStaggeredGridScope.items to present list of items or LazyStaggeredGridScope.item for a single one.

rememberLazyStaggeredGridState

@Composable
fun rememberLazyStaggeredGridState(
    initialFirstVisibleItemIndex: Int = 0,
    initialFirstVisibleItemScrollOffset: Int = 0
): LazyStaggeredGridState

Creates a LazyStaggeredGridState that is remembered across composition.

Calling this function with different parameters on recomposition WILL NOT recreate or change the state. Use LazyStaggeredGridState.scrollToItem or LazyStaggeredGridState.animateScrollToItem to adjust position instead.

Parameters
initialFirstVisibleItemIndex: Int = 0

initial position for LazyStaggeredGridState.firstVisibleItemIndex

initialFirstVisibleItemScrollOffset: Int = 0

initial value for LazyStaggeredGridState.firstVisibleItemScrollOffset

Returns
LazyStaggeredGridState

created and memoized LazyStaggeredGridState with given parameters.

Extension functions

inline fun <T : Any?> LazyStaggeredGridScope.items(
    items: Array<T>,
    noinline key: ((item) -> Any)? = null,
    crossinline contentType: (item) -> Any = { null },
    noinline span: ((item) -> StaggeredGridItemSpan)? = null,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit
): Unit

Add an array of items to the staggered grid.

Parameters
items: Array<T>

a data array to present

noinline key: ((item) -> Any)? = null

a factory of stable and unique keys representing the item. The key MUST be saveable via Bundle on Android. If set to null (by default), the position of the item will be used as a key instead. Using the same key for multiple items in the staggered grid is not allowed. This can be overridden by calling LazyStaggeredGridState.requestScrollToItem.

When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one.

crossinline contentType: (item) -> Any = { null }

a factory of content types representing the item. Content for item of the same type can be reused more efficiently. null is a valid type as well and items of such type will be considered compatible.

noinline span: ((item) -> StaggeredGridItemSpan)? = null

a factory of custom spans for this item. Spans configure how many lanes defined by StaggeredGridCells the item will occupy. By default each item will take one lane.

crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit

composable content displayed by the provided item

inline fun <T : Any?> LazyStaggeredGridScope.items(
    items: List<T>,
    noinline key: ((item) -> Any)? = null,
    crossinline contentType: (item) -> Any = { null },
    noinline span: ((item) -> StaggeredGridItemSpan)? = null,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit
): Unit

Add a list of items to the staggered grid.

Parameters
items: List<T>

a data list to present

noinline key: ((item) -> Any)? = null

a factory of stable and unique keys representing the item. The key MUST be saveable via Bundle on Android. If set to null (by default), the position of the item will be used as a key instead. Using the same key for multiple items in the staggered grid is not allowed. This can be overridden by calling LazyStaggeredGridState.requestScrollToItem.

When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one.

crossinline contentType: (item) -> Any = { null }

a factory of content types representing the item. Content for item of the same type can be reused more efficiently. null is a valid type as well and items of such type will be considered compatible.

noinline span: ((item) -> StaggeredGridItemSpan)? = null

a factory of custom spans for this item. Spans configure how many lanes defined by StaggeredGridCells the item will occupy. By default each item will take one lane.

crossinline itemContent: @Composable LazyStaggeredGridItemScope.(item) -> Unit

composable content displayed by the provided item

itemsIndexed

inline fun <T : Any?> LazyStaggeredGridScope.itemsIndexed(
    items: Array<T>,
    noinline key: ((index: Int, item) -> Any)? = null,
    crossinline contentType: (index: Int, item) -> Any = { _, _ -> null },
    noinline span: ((index: Int, item) -> StaggeredGridItemSpan)? = null,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit
): Unit

Add an array of items with index-aware content to the staggered grid.

Parameters
items: Array<T>

a data array to present

noinline key: ((index: Int, item) -> Any)? = null

a factory of stable and unique keys representing the item. The key MUST be saveable via Bundle on Android. If set to null (by default), the position of the item will be used as a key instead. Using the same key for multiple items in the staggered grid is not allowed. This can be overridden by calling LazyStaggeredGridState.requestScrollToItem.

When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one.

crossinline contentType: (index: Int, item) -> Any = { _, _ -> null }

a factory of content types representing the item. Content for item of the same type can be reused more efficiently. null is a valid type as well and items of such type will be considered compatible.

noinline span: ((index: Int, item) -> StaggeredGridItemSpan)? = null

a factory of custom spans for this item. Spans configure how many lanes defined by StaggeredGridCells the item will occupy. By default each item will take one lane.

crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit

composable content displayed given item and index

inline fun <T : Any?> LazyStaggeredGridScope.itemsIndexed(
    items: List<T>,
    noinline key: ((index: Int, item) -> Any)? = null,
    crossinline contentType: (index: Int, item) -> Any = { _, _ -> null },
    noinline span: ((index: Int, item) -> StaggeredGridItemSpan)? = null,
    crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit
): Unit

Add a list of items with index-aware content to the staggered grid.

Parameters
items: List<T>

a data list to present

noinline key: ((index: Int, item) -> Any)? = null

a factory of stable and unique keys representing the item. The key MUST be saveable via Bundle on Android. If set to null (by default), the position of the item will be used as a key instead. Using the same key for multiple items in the staggered grid is not allowed. This can be overridden by calling LazyStaggeredGridState.requestScrollToItem.

When you specify the key the scroll position will be maintained based on the key, which means if you add/remove items before the current visible item the item with the given key will be kept as the first visible one.

crossinline contentType: (index: Int, item) -> Any = { _, _ -> null }

a factory of content types representing the item. Content for item of the same type can be reused more efficiently. null is a valid type as well and items of such type will be considered compatible.

noinline span: ((index: Int, item) -> StaggeredGridItemSpan)? = null

a factory of custom spans for this item. Spans configure how many lanes defined by StaggeredGridCells the item will occupy. By default each item will take one lane.

crossinline itemContent: @Composable LazyStaggeredGridItemScope.(index: Int, item) -> Unit

composable content displayed given item and index