About this widget
To use this widget to display a paginated list of results from multiple categories, you need these components:MultiSearcher. TheSearcherthat handles your searchesFilterState. Tracks any applied filters (such as genre or year) and updates search results when the changes.HitsState. Hits UI stateSearchBoxState. Search box UI state
Explore example code
Browse the Multi Hits example code on GitHub.
Examples
Activity
Kotlin
class SearchActivity : ComponentActivity() {
private val viewModel: SearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
SearchScreen(
searchBoxState = viewModel.searchBoxState,
actorsState = viewModel.actorsState,
moviesState = viewModel.moviesState,
)
}
}
}
}
View model
Kotlin
import com.algolia.client.model.search.SearchParamsObject
class SearchViewModel : ViewModel() {
private val multiSearcher = MultiSearcher(
applicationID = "YourApplicationID",
apiKey = "YourAPIKey"
)
private val actorsSearcher = multiSearcher.addHitsSearcher(
indexName = "YourActorsIndex",
query = SearchParamsObject(hitsPerPage = 5)
)
private val moviesSearcher = multiSearcher.addHitsSearcher(indexName = "YourMoviesIndex")
private val searchBoxConnector = SearchBoxConnector(multiSearcher)
private val connections = ConnectionHandler(searchBoxConnector)
val searchBoxState = SearchBoxState()
val actorsState = HitsState<Actor>()
val moviesState = HitsState<Movie>()
init {
connections += searchBoxConnector.connectView(searchBoxState)
connections += actorsSearcher.connectHitsView(actorsState) { it.hits.deserialize(Actor.serializer()) }
connections += moviesSearcher.connectHitsView(moviesState) { it.hits.deserialize(Movie.serializer()) }
multiSearcher.searchAsync()
}
override fun onCleared() {
super.onCleared()
multiSearcher.cancel()
connections.clear()
}
}
UI
Kotlin
@Composable
fun SearchScreen(
modifier: Modifier = Modifier,
searchBoxState: SearchBoxState,
actorsState: HitsState<Actor>,
moviesState: HitsState<Movie>,
) {
val scrollState = rememberScrollState()
Column(
modifier = modifier
.fillMaxWidth()
.verticalScroll(scrollState)
) {
SearchBox(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
searchBoxState = searchBoxState,
)
Actors(actors = actorsState.hits)
Movies(movies = moviesState.hits)
}
}
@Composable
private fun Actors(
modifier: Modifier = Modifier,
actors: List<Actor>,
) {
if (actors.isEmpty()) return
Column(modifier) {
SectionTitle(
modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 4.dp),
title = "Actors"
)
actors.forEach { actor ->
Actor(actor = actor)
}
}
}
@Composable
private fun Actor(
modifier: Modifier = Modifier,
actor: Actor
) {
Row(
modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(horizontal = 24.dp, vertical = 12.dp)
) {
Icon(
imageVector = Icons.Default.AccountCircle,
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.2f),
contentDescription = null
)
Text(
text = actor.highlightedName?.toAnnotatedString() ?: AnnotatedString(actor.name),
modifier = Modifier.padding(start = 12.dp),
)
}
}
@Composable
private fun Movies(
modifier: Modifier = Modifier,
movies: List<Movie>
) {
if (movies.isEmpty()) return
Column(modifier) {
SectionTitle(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
title = "Movies"
)
movies.forEach { movie ->
Movie(movie = movie)
}
}
}
@Composable
private fun Movie(modifier: Modifier = Modifier, movie: Movie) {
Row(
modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(horizontal = 24.dp, vertical = 12.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.2f),
contentDescription = null
)
Text(
text = movie.highlightedTitle?.toAnnotatedString() ?: AnnotatedString(movie.title),
modifier = Modifier.padding(start = 12.dp),
)
}
}
@Composable
private fun SectionTitle(modifier: Modifier = Modifier, title: String) {
Text(
modifier = modifier,
text = title, style = MaterialTheme.typography.subtitle2,
color = MaterialTheme.colors.onBackground.copy(alpha = 0.4f),
)
}
Models
Kotlin
import com.algolia.instantsearch.core.Indexable
@Serializable
data class Actor(
val name: String,
override val objectID: String,
override val _highlightResult: JsonObject?
) : Indexable, Highlightable {
val highlightedName
get() = getHighlight("name")
}
Kotlin
import com.algolia.instantsearch.core.Indexable
@Serializable
data class Movie(
val title: String,
override val objectID: String,
override val _highlightResult: JsonObject?
) : Indexable, Highlightable {
val highlightedTitle
get() = getHighlight("title")
}