Skip to content

How to Implement Search in Compose

Updated: at 08:11 PM

Demo

In this tutorial, you will learn how to implement search functionality in compose for static data without using viewmodel. Let’s start!

Table of contents

Open Table of contents

Scenario

We’re building a Currency Converter app where user can select a currency he wants to convert. And for this purposoe, we show a dialog with a list of currencies and user can select any currency from the list. To further improve user experience, we’ll add a search bar.

Data Model

Before we begin, let’s take a look at the data model that we have:

@Immutable
data class Currency(
    val code: String,
    val name: String,
    val symbol: String
)

We represent a single currency using this model. We will filter out this currencies by comparing the name and code with user’s query.

UI

@Composable
private fun CurrencyPickerContent(
    currencies: List<Currency>,
    onSelected: (currency: Currency) -> Unit,
    modifier: Modifier = Modifier,
    selectedCurrency: Currency? = null,
) {
    var query by remember { mutableStateOf("") }
    var filteredCurrencies by remember { mutableStateOf(currencies) }

    // TODO: Implement Search

    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(8.dp)
    ) {
        LazyColumn(
            modifier = modifier.padding(8.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            stickyHeader {
                Surface(
                    modifier = Modifier.fillMaxWidth(),
                    color = MaterialTheme.colorScheme.surface
                ) {
                    TextField(
                        value = query,
                        onValueChange = { query = it },
                        shape = RoundedCornerShape(4.dp),
                        modifier = Modifier.padding(bottom = 8.dp)
                    )
                }
            }

            items(
                items = filteredCurrencies,
            ) {
                CurrencyCard(
                    currency = it,
                    onClick = { onSelected(it) },
                    modifier = Modifier.fillMaxWidth(),
                    selected = selectedCurrency?.code == it.code
                )
            }
        }
    }
}

Here we have a composable function that shows a list of currencies with a search bar at the top.

In our composable, we have two state variables:

We can easily implement search by using Kotlin’s flow api and LaunchedEffect:

LaunchedEffect(currencies) {
  snapshotFlow { query }
    .debounce(300)
    .distinctUntilChanged()
    .mapLatest {
        currencies.filter { currency ->
            currency.name.contains(it.trim(), ignoreCase = true) || currency.code.contains(it.trim(), ignoreCase = true)
        }
    }
    .flowOn(Dispatchers.Default)
    .collectLatest { filteredCurrencies = it }
}

We’re using snapshotFlow to convert user query into a flow and apply different flow operators such as debounce, mapLatest etc. At the end, we update the filtered currencies and the new currencies are displayed.

Source Code