Interactions
Within Litho, lists are implemented using the Lazy Collection API.
Clicking items​
Clicking items in the Lazy Collection can be done by simply adding Style.onClick to children.
private val ONE_TO_TEN =
listOf("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten")
class BasicList : KComponent() {
override fun ComponentScope.render(): Component {
val clickItem = useState { "Nothing" }
val callBack = useCallback { index: Int -> clickItem.update(ONE_TO_TEN[index]) }
return LazyList {
child(Text(text = "Click us!"))
ONE_TO_TEN.forEachIndexed { index, str ->
child(
id = str,
component =
Text(
str,
style = Style.onClick { callBack(index) }.padding(all = 16.dp),
))
}
child(Text(text = "${clickItem.value} was clicked"))
}
}
}
Scrolling the Lazy Collection​
Scrolling the Lazy Collection by a given distance, or to a child with a given index/id, is a two-step process:
- Create a
LazyCollectionControllerand attach it to the Lazy Collection via thelazyCollectionControllerparameter. Note that it should be defined in auseStatehook. - Use one of the
LazyCollectionController’s scroll functions.
class ScrollingExample() : KComponent() {
override fun ComponentScope.render(): Component {
val controller = useLazyCollectionController()
// Use one of these lambdas to scroll, e.g. in an onClick callback
val scrollToTenth = useCallback<ClickEvent, Unit> { controller.scrollToIndex(index = 10) }
val smoothScrollToEnd = useCallback<ClickEvent, Unit> { controller.smoothScrollToId("End") }
return LazyList(
lazyCollectionController = controller,
) {
child(Text(style = Style.onClick(scrollToTenth), text = "Scroll to item 10"))
child(Text(style = Style.onClick(smoothScrollToEnd), text = "Smooth Scroll to End"))
children(items = (0..20), id = { it }) { Text("$it") }
child(id = "End", component = Text("End"))
}
}
}
Responding to scroll events​
An OnNearCallback can be applied to the Lazy Collection's onNearEnd parameter, or a child's onNearViewport parameter.
For example, onNearEnd = OnNearCallback(offset = 10) { /* callback */ }.
Where:
onNearEndis invoked when the Lazy Collection is scrolled to the last position or is scrolled withinoffsetitems away.onNearViewportis invoked when the child enters the viewport or isoffsetitems away.
For more complex scroll handling (such as for animations), you can access the RecyclerView directly via a LazyCollectionController and use a RecyclerView.OnScrollListener to receive callbacks during scroll. To use this technique, take the following steps:
- Create a
LazyCollectionControllerand attach it to the Lazy Collection via thelazyCollectionControllerparameter. Note that it should be defined in auseStatehook. - Access the
RecyclerViewviaLazyCollectionController.recyclerView - Add a custom
OnScrollListenerusingRecyclerView.addOnScrollListener(..)
It is currently unsafe to trigger a scroll event inside a useEffect callback as it will be invoked before the Lazy Collection's contents have been mounted.
To trigger a scroll immediately on entering a screen, use the LazyCollection's onDataBound callback.
Paging​
Lists of data are often retrieved from the server in pages; additional data is requested only if the user scrolls. To achieve this behaviour, add a callback to the Lazy Collection’s onNearEnd parameter that fetches more data. This will be triggered when the Lazy Collection is scrolled near to the end. Optionally, add a request indicator to the bottom of the list.
class PagedExample(private val pagedList: PaginatedList<Item>) : KComponent() {
override fun ComponentScope.render(): Component =
LazyList(
onNearEnd = OnNearCallback { if (pagedList.hasNextPage) pagedList.fetchNextPage() },
) {
// Add the retrieved items
children(items = pagedList.list, id = { it.id }) { Text(it.text) }
// Optionally add a progress spinner
if (pagedList.hasNextPage) {
child(ProgressSpinner())
}
}
}
Pull to refresh​
To implement the 'pull to refresh' behaviour, provide a callback to the Lazy Collection's onPullToRefresh parameter. This callback will be responsible for fetching fresh data.
To dismiss the refreshing indicator, take the following two steps:
- Create a
LazyCollectionControllerand attach it to the Lazy Collection via thelazyCollectionControllerparameter. Note that it should be defined in auseStatehook. - Call
LazyCollectionController.setRefreshing(false).
class PullToRefreshExample(
val data: List<String>,
val refresh: () -> Unit,
) : KComponent() {
override fun ComponentScope.render(): Component {
val controller = useLazyCollectionController()
return LazyList(
lazyCollectionController = controller,
onPullToRefresh = {
refresh()
controller.setRefreshing(false)
},
) { /* Add children */
}
}
}