Implement Instant Search Using Kotlin Flow Operators
A flow is a type in coroutines that can emit multiple values sequentially, as opposed to suspend functions, which only return a single value. A flow, for example, can be used to receive real-time updates from a database. Flows are constructed on top of coroutines and can return multiple values. A flow is a stream of data that can be computed asynchronously. All the outputs of the flow must be of the same type. A FlowInt, for example, is a flow that emits integer values. A flow is similar to an Iterator in that it produces a sequence of values, but it produces and consumes values asynchronously by using suspend functions. This means that the flow, for example, can safely make a network request to generate the next value without blocking the main thread.
The following Kotlin Flow features will be used to implement this search feature:
- StateFlow: We’ve already written an article about it. You can find out more about it right here.
- Operator of a Debounce
- Operator of a Filter
- Operator DistinctUntilChanged
- Latest FlatMap Operator
Previously, implementing this instant search feature in Android was difficult with Kotlin Coroutines, but with Kotlin Flow Operators, it has become simple and interesting.
Let’s get started
First and foremost, we will write an extension function that returns the StateFlow so that we can apply the necessary operators to it. So, on the SearchView, we’ll use the setOnQueryTextListener to watch for changes in the text, change the query’s state, and finally return the StateFlow as shown below:
Then in order to test this, we take some pseudo network exchange for the data.
Now, on the QueryTextChangeStateFlow, we will use the following operators:
It’s time to learn why the above operators are used and how they work when combined.
- Debounce: The debounce operator is used with a time constant in this case. When the user types “a”, “ab”, or “abc” in a short period of time, the debounce operator handles the case. As a result, there will be a large number of network calls. However, the user is ultimately interested in the “abc” search result. As a result, the results of “a” and “ab” must be discarded. There should ideally be no network calls for “a” and “ab” because the user typed those in a very short period of time.
- Filter: In this case, the filter operator is used to filter out the unwanted string, which is an empty string, in order to avoid making an unnecessary network call.
- The distinctUntilChanged: Simply used to avoid duplicate calls over the desired network layer. Assume the most recent ongoing search query was “abc,” and the user deleted “c” before typing “c” again. So it’s “abc” once more. Otherwise, any further network call will make the search return a duplicate query increasing the load on the server and thus preventing the source from emitting duplicate consecutive items.
- FlatMapLatest: This is used so that all the pre-fetched stale results are not shown to the users in a situation like a network drop. Assume the most recent search query was “ab,” there is an ongoing network call for “ab,” and the user typed “abc.” Then we’re no longer interested in the outcome of “ab.” We are only concerned with the outcome of “abc.” As a result, flatMapLatest comes to the rescue. It only returns the results of the most recent search query and ignores the rest.
GeekTip: Please keep in mind that if there is an error in the flatMapLatest, we pass the empty result. This can be changed based on our needs.
In this manner, we can implement the instant search feature in an Android application using Kotlin Flow Operators.