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
LazyCollectionController
and attach it to the Lazy Collection via thelazyCollectionController
parameter. Note that it should be defined in auseState
hook. - 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:
onNearEnd
is invoked when the Lazy Collection is scrolled to the last position or is scrolled withinoffset
items away.onNearViewport
is invoked when the child enters the viewport or isoffset
items 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
LazyCollectionController
and attach it to the Lazy Collection via thelazyCollectionController
parameter. Note that it should be defined in auseState
hook. - Access the
RecyclerView
viaLazyCollectionController.recyclerView
- Add a custom
OnScrollListener
usingRecyclerView.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
LazyCollectionController
and attach it to the Lazy Collection via thelazyCollectionController
parameter. Note that it should be defined in auseState
hook. - 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 */
}
}
}