Skip to main content

About this widget

A view with helpers that displays a paginated list of search results. It uses Android Architecture Components’ Paging library and LiveData to provide lifecycle-aware, observable search results that can be loaded as users scroll. To add infinite hits to your search experience, add the following to your build.gradle file:
implementation 'com.algolia:instantsearch-android-paging3:3.+'
Use these components:
  • Searcher to handle your searches.
  • Paginator. A pagination utility that will load hits incrementally.
  • PagingConfig to configure loading behavior.
  • T. A data class representing a search result.
  • FilterState so that the paginated list refreshes when filters change.
As an alternative to this approach, the infinite scroll guide describes how to create an automatically scrolling infinite hits experience. See also: Searches without results

Examples

Kotlin
class MyActivity : AppCompatActivity() {

    val searcher = HitsSearcher(
        applicationID = ApplicationID("YourApplicationID"),
        apiKey = APIKey("YourSearchOnlyAPIKey"),
        indexName = IndexName("YourIndexName")
    )
    val paginator = Paginator(
        searcher = searcher,
        pagingConfig = PagingConfig(pageSize = 10),
        transformer = { it.deserialize(Movie.serializer()) }
    )
    val filterState = FilterState()
    val adapter = MovieAdapter()
    val connection = ConnectionHandler()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        connection += filterState.connectPaginator(paginator)
        paginator.liveData.observe(this) { adapter.submitData(lifecycle, it) }
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        searcher.cancel()
        connection.disconnect()
    }
}

@Serializable
data class Movie(
    val title: String
)

class MovieViewHolder(val view: TextView) : RecyclerView.ViewHolder(view) {

    fun bind(data: Movie) {
        view.text = data.title
    }
}

class MovieAdapter : PagingDataAdapter<Movie, MovieViewHolder>(MovieDiffUtil) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
        return MovieViewHolder(TextView(parent.context))
    }

    override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
        val movie = getItem(position)

        if (movie != null) holder.bind(movie)
    }

    object MovieDiffUtil : DiffUtil.ItemCallback<Movie>() {

        override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            return oldItem.title == newItem.title
        }
    }
}

Compose UI

InstantSearch provides the Paginator, which exposes a flow property for paging data and an invalidate() method to stop loading. You must also connect Paginator to other components, such as SearchBoxConnector, FilterState, or FacetListConnector.
Kotlin
class MyActivity : AppCompatActivity() {
    val searcher = HitsSearcher(
        applicationID = ApplicationID("YourApplicationID"),
        apiKey = APIKey("YourSearchOnlyAPIKey"),
        indexName = IndexName("YourIndexName")
    )
    val pagingConfig = PagingConfig(pageSize = 10)
    val hitsPaginator = Paginator(searcher, pagingConfig) { it.deserialize(Movie.serializer()) }
    val searchBoxState = SearchBoxState()
    val searchBox = SearchBoxConnector(searcher)
    val connections = ConnectionHandler()

    init {
        connections += searchBox.connectView(searchBoxState)
        connections += searchBox.connectPaginator(hitsPaginator)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SearchBox(
                searchBoxState = searchBoxState
            )
            val hitsPaging = hitsPaginator.flow.collectAsLazyPagingItems()
            LazyColumn {
                items(hitsPaging) { movie ->
                    movie?.let { Text(it.title) }
                }
            }
        }
        searcher.searchAsync()
    }

    override fun onDestroy() {
        super.onDestroy()
        connections.disconnect()
        searcher.cancel()
    }
}
I