Create your own InstantSearch Android widgets
On this page
If none of the existing widgets fit your use case, you can implement your own.
You are trying to create your own widget with InstantSearch Android and that’s awesome. But that also means that you couldn’t find the widgets or built-in options you were looking for. Algolia would love to hear about your use case as the aim with the InstantSearch libraries is to provide the best out-of-the-box experience. Don’t hesitate to send a quick message explaining what you were trying to achieve either using the form at the end of that page or directly by submitting a feature request.
Creating a widget takes three steps:
- Create the
MyWidgetViewModel
, containing the business logic for your widget. - Create a
MyWidgetView
interface, describing the rendering of the widget data.- Implement your view in a
MyWidgetViewImpl
that you’ll use.
- Implement your view in a
- Create the
Connection
s between yourViewModel
and the other components:- Create a
MyWidgetConnectionView
to connect yourViewModel
to itsView
. - If it uses the
Searcher
, create aMyWidgetConnectionSearcher
. - If it uses the
FilterState
, create aMyWidgetConnectionFilterState
.
- Create a
Example
You’ll build a widget that displays the number of searches made since it was last clicked.
Create the ViewModel
Our ViewModel
will be quite straightforward: it stores a sum
that can be increment
ed or reset
ed to 0.
We will use InstantSearch’s SubscriptionValue
to allow subscribing to changes of the sum
’s value.
1
2
3
4
5
6
7
8
9
10
11
12
class SumSearchesViewModel {
val sum = SubscriptionValue(0)
fun increment() {
sum.value++
}
fun reset() {
sum.value = 0
}
}
Create the View
interface
To interact with the data in our ViewModel
, you need a view than can display a number, and handle clicks for resetting.
1
2
3
4
5
interface SumSearchesView {
fun setSum(sum: Int) // will be called on new sum
var onReset: Callback<Unit>? // will hold the callback to reset the sum
}
Implementing our View
We can now implement a SumSearchesView
: it should display the data received in setSum
and trigger the onReset
when clicked.
1
2
3
4
5
6
7
8
9
10
11
12
class SumSearchesViewImpl(val view: TextView) : SumSearchesView {
init {
view.setOnClickListener { onReset?.invoke(Unit) }
}
override fun setSum(sum: Int) {
view.text = "$sum searches."
}
override var onReset: Callback<Unit>? = null
}
Create the ConnectionView
To link our ViewModel
and its View
, we will define a connection to describe what should happen when connecting (subscribe to sum
and set the reset callback) and when disconnecting (unsubscribe to sum
and remove the callback).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data class SumSearchesConnectionView(
private val viewModel: SumSearchesViewModel,
private val view: SumSearchesView
) : ConnectionImpl() {
private val updateViewSum: (Int) -> Unit = {
view.setSum(it)
}
override fun connect() {
super.connect()
viewModel.sum.subscribePast(updateViewSum)
view.onReset = { viewModel.reset() }
}
override fun disconnect() {
super.disconnect()
viewModel.sum.unsubscribe(updateViewSum)
view.onReset = null
}
}
Create the ConnectionSearcher
Because our widget needs to be aware of searches to count them, it needs to be connected to a Searcher
.
Subscribe to its response
to call increment()
on every new search response.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data class SumSearchesConnectionSearcher(
private val viewModel: SumSearchesViewModel,
private val searcher: HitsSearcher
) : ConnectionImpl() {
private val updateSum: (ResponseSearch?) -> Unit = {
viewModel.increment()
}
override fun connect() {
super.connect()
searcher.response.subscribe(updateSum)
}
override fun disconnect() {
super.disconnect()
searcher.response.unsubscribe(updateSum)
}
}
Convenient functions
To simplify usage of the widget, you will create two extension functions that connect the ViewModel
to other components:
1
2
3
4
5
6
7
8
9
10
11
fun SumSearchesViewModel.connectView(
view: SumSearchesView
): Connection {
return SumSearchesConnectionView(this, view)
}
fun SumSearchesViewModel.connectSearcher(
searcher: HitsSearcher
): Connection {
return SumSearchesConnectionSearcher(this, searcher)
}
Final touches
You just created your first custom widget and you can now use it in your application like any other widget:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val searcher = HitsSearcher(client, index) // Initialize your Searcher as usual
val view = TextView(context) // Create or find the view you want to use
// Create a connectionHandler to hold all connections
val connection = ConnectionHandler()
// Create your ViewModel and View implementation
val viewModel = SumSearchesViewModel()
val sumView = SumSearchesViewImpl(view)
// Connect your ViewModel to start displaying the count of searches
connection += viewModel.connectSearcher(searcher)
connection += viewModel.connectView(sumView)
// When you want to disconnect everything and ensure no memory leak
// for example in your Activity's onDestroy()`
connection.disconnect()