Reputation: 2599
Recently LazyLayout
for compose was shared as experimental to try things out. It's used by other lazy implementations for list (column / row) and grid. I wanted to try this out and implement a simple custom lazy layout to consume scrolling and display items lazily (similarly to LazyList
). The code below is dummy, but the idea is to do something on scrolling offset (in this example I'm placing 3 items with a space of scroll offset).
The problem is that on scroll all composables disappear with layout
block being called but it doesn't place anything (only initial composition). The answer probably lies somewhere in LazyList
or LazyGrid
, but I wanted to avoid copy paste and do something incrementally. I'd appreciate if someone could shed some light on this issue, how to actually handle scrolling with LazyLayout
.
I tried replacing scrollable
with verticalScroll
but it gives same results.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun CustomLazyLayout(
modifier: Modifier = Modifier,
content: @Composable (String) -> Unit
) {
val scrollState = rememberScrollState()
var firstId by remember { mutableStateOf(0) }
var firstOffset by remember { mutableStateOf(0) }
LazyLayout(
modifier = modifier
.clipScrollableContainer(Orientation.Vertical)
.scrollable(
orientation = Orientation.Vertical,
reverseDirection = false,
interactionSource = null,
flingBehavior = ScrollableDefaults.flingBehavior(),
state = scrollState,
overscrollEffect = ScrollableDefaults.overscrollEffect(),
enabled = true
),
itemProvider = object : LazyLayoutItemProvider {
override val itemCount: Int
get() = 10
@Composable
override fun Item(index: Int) {
content("$index")
}
},
measurePolicy = { constraints ->
val list = mutableListOf<Placeable>()
var i = firstId
while(i < firstId+3) {
val m = measure(i, constraints)
if (m.isNotEmpty()) {
list.add(m[0])
}
++i
}
val height = list.first().measuredHeight
var positionY = 0
layout(constraints.maxWidth, constraints.maxHeight) {
for (placeable in list) {
placeable.place(0, positionY + scrollState.value)
positionY += height
}
}
}
)
Upvotes: 4
Views: 1536
Reputation: 2599
Problem is solved if I move scrollableState
outside of layout
block to measurePolicy
. To be precise this code will actually mimic regular scroll effect:
var positionY = -scrollState.value
layout(constraints.maxWidth, constraints.maxHeight) {
for (placeable in list) {
placeable.place(0, positionY)
positionY += height
}
}
Previously on scroll only layout
block was called. Now both are (measure and layout). I'm still quite unsure if I'm doing it the right way, but items are composed only once so I guess it's working.
Upvotes: 2