Have anyone a working example with compose and Room viewing a list in a VerticalScroller setting (or...
r
Have anyone a working example with compose and Room viewing a list in a VerticalScroller setting (or any other composable) which is imitating a recyclerview? Very interesting in how to detect where/when/how to add items the best way to the VerticalScroller when scrolling, and how to update data contents to the existing items in the list the best way ?
b
maybe this will help
Copy code
@Composable
fun Scroller(
    onNextPage: (page: Int) -> Unit,
    child: @Composable() () -> Unit
) {
    val scrollerPosition: ScrollerPosition = +memo { scroller }

    VerticalScroller(scrollerPosition, { pos: Px, maxPos: Px ->
        scrollerPosition.value = pos

        if (pos.value > maxPos.value - 500) {
            onNextPage(currentPage)
            currentPage++
        }
    }) {
        child()
    }
}
l
In general this is a very difficult problem, especially if you want to do it right. We are starting work on a component that does this, but it will take some time to get right. In the meantime, you might want to seek inspiration from virtualized list views that the react or react native community have created to see if a similar approach could be done in compose. It probably can, but our approach will probably leverage some deeper implementation details of compose in order to get even better performance
m
Can we see the wip on gerrit?
l
We're not intentionally hiding anything :) work on this like just started. I'm sure some CLs will be coming in soon
r
@Bruno_ In your codesnip is currentPage a gloal variable, or is it a typo (missing declaration) ?
b
@Roar Gronmo thanks for noticing it, it should be just a plain var (assuming that you reload the state somewhere else, for example I add items to a ModelList)
Copy code
@Composable
fun PagedVerticalScroller(
    onNextPage: (page: Int) -> Unit,
    child: @Composable() () -> Unit
) {
    var currentPage = 0
    val scrollerPosition: ScrollerPosition = +memo { ScrollerPosition() }

    VerticalScroller(scrollerPosition, { pos: Px, maxPos: Px ->
        scrollerPosition.value = pos

        if (pos.value > maxPos.value - 500) {
            onNextPage(currentPage)
            currentPage++
        }
    }) {
        child()
    }
}
r
@Bruno_ Well, when testing this approach, I see that currentPage will trigger for every movement above maxPosx-500, si I think you need to use an enum to handle the reload states, depends if you need to do a lazy_read or similar (idle, ready, reading, error, warning (no_data), done). It would have been very nice if xxxScroller had a trigger (onEdge) which could be set (similar to this approach) but was only fired once when it passed maxPosx-triggerDistance. Of course, you need to reset that trigger when data was achieved. Secondly you could run into an onEdge race. RG
@Bruno_ Do you know if onScrolledPositionChanged is called sequentally or is called asynchroniosly; the risk of several simultaniously calls, due to movement, and slow performance when calling heavy functions in onScrollPositionChanged ?
b
@Roar Gronmo yeah, when scrolling the onPageChange is called 2-3 times throttling could be added to the implementation to prevent that
I think it's async but I may be wrong
m
This is what i came up with: https://gist.github.com/IVIanuu/665aeb760c344493a26aa9fa47d5ae56 It composes only the necessary items to fill the viewport. It can be used like this:
Copy code
val items = (1..100_000).toList()  
ScrollableList(
    items = items,
    itemSizeProvider = { 48.dp }
) { _, item ->
      ListItem(text = +memo { "Item $item" })
}
The only downside is that it needs to know the size of each item in advance.
r
Ok...
m
I changed the code a bit maybe it's easier to follow.
r
@Manuel Wrage Hi, I found that updating a mutable list outside the onScrollPositionChanged (i.e. clicking a button to update the list) the contents of the VerticalScroller got updated correctly. If I updated the mutable list inside the onScrollPositionChanged the VerticalScroller would not be updated. If the Button based update was executed ("onClick") I got a flood of all the stacked up changes done inside onScrollPosition. May the reason be that the change on the @Model related data arrived and was handled before onScrollPosition finished... ?