Skip to main content

Interactions

note

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:

  1. Create a LazyCollectionController and attach it to the Lazy Collection via the lazyCollectionController parameter. Note that it should be defined in a useState hook.
  2. Use one of the LazyCollectionController’s scroll functions.
class ScrollingExample() : KComponent() {

override fun ComponentScope.render(): Component {
val controller = useState { LazyCollectionController() }.value

// Use one of these lambdas to scroll, e.g. in an onClick callback
val scrollToTenth = controller.scrollToIndex(10)
val smoothScrollToEnd = controller.smoothScrollToId("End")

return LazyList(
lazyCollectionController = controller,
) {
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 within offset items away.
  • onNearViewport is invoked when the child enters the viewport or is offset 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:

  1. Create a LazyCollectionController and attach it to the Lazy Collection via the lazyCollectionController parameter. Note that it should be defined in a useState hook.
  2. Access the RecyclerView via LazyCollectionController.recyclerView
  3. Add a custom OnScrollListener using RecyclerView.addOnScrollListener(..)
note

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:

  1. Create a LazyCollectionController and attach it to the Lazy Collection via the lazyCollectionController parameter. Note that it should be defined in a useState hook.
  2. Call LazyCollectionController.setRefreshing(false).
class PullToRefreshExample(
val data: List<String>,
val refresh: () -> Unit,
) : KComponent() {

override fun ComponentScope.render(): Component {
val controller = useState { LazyCollectionController() }.value
return LazyList(
lazyCollectionController = controller,
onPullToRefresh = {
refresh()
controller.setRefreshing(false)
},
) { /* Add children */}
}
}