The ability to search for content within an app is a common feature, in fact, you’ll find it somewhere within most applications on your device. On Android, a common UI component we see for this functionality is a floating search bar, placed in a prominent part of the screen. In some cases, this also provides search recommendations to the user to streamline the search process. The Jetpack Compose Material3 package provides access to a SearchBar composable that offers this exact functionality and in this blog post, we’re going to learn how to utilise it in our own apps.

The SearchBar composable allows us to display a floating Search component, which expands to display selectable recommendations. As mentioned above, this is a common pattern that we see in many applications, with this composable offering an out-of-the-box solution. The SearchBar composable provides enough customisation to control the look and feel of the component, along with using a slot-based approach for us to provide the input field for use.
@Composable
fun SearchBar(
inputField: @Composable () -> Unit,
expanded: Boolean,
onExpandedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
shape: Shape = SearchBarDefaults.inputFieldShape,
colors: SearchBarColors = SearchBarDefaults.colors(),
tonalElevation: Dp = SearchBarDefaults.TonalElevation,
shadowElevation: Dp = SearchBarDefaults.ShadowElevation,
windowInsets: WindowInsets = SearchBarDefaults.windowInsets,
content: @Composable ColumnScope.() -> Unit,
)
The component handles most of the internals for us – with two key parts needing to be provided by ourselves.
- inputField – this is the input composable that represents the search field for content input
- content – this is the content area used to display recommendations when the search bar is expanded
Outside of these fields, there are a collection of other properties that are used to determine the current state of the SearchBar. For example, when the Search Bar is in an expanded state, the content of the composable will be displayed beneath the input field. To be able to manage this, we need to provide some arguments to the composable that will be used to manage this state. To start with, the expanded argument is used to depict whether the SearchBar is in an expanded state (which will decide whether the content area will be shown), along with the onExpandedChange argument which is used to provide the implementation with an updated value for the expanded state (which can then be used to reflect our own state implementation).
var expanded by remember { mutableStateOf(false) }
SearchBar(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onExpandedChange = {
expanded = it
}
)
Alongside managing this expanded state, we need to provide the inputField that is to be used for the input area of the SearchBar. Outside of following the slot-based approach for composables, this allows the composable to follow the concepts of state hoisting, allowing us to completely manage the state concepts for the input field of the SearchBar.
var expanded by remember { mutableStateOf(false) }
var query by remember { mutableStateOf<String?>(null) }
SearchBar(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onExpandedChange = {
expanded = it
},
inputField = {
}
)
To make this simpler, the SearchBarDefaults class provides us access to an InputField composable – this gives us access to a composable specifically implemented for use with the SearchBar. It is not required to utilise this specific composable, but it is provided as a convenience composable specifically for a search-based input field. This composable takes some key arguments that are used to configure it for use within the SearchBar:
- expanded and onExpandedChange – used to manage the expanded state of the field
- query and onQueryChange – used to manage the state of the query displayed in the field
Alongside these core properties, you’ll also notice the support for standard field arguments such as the placeholder, leadingIcon and trailingIcon. Alongside these being used for informative purposes, we can see in the example below how I have used the trailingIcon to allow the SearchBar to revert back to the collapsed state when the cancel button is clicked.
SearchBarDefaults.InputField(
onSearch = { expanded = false },
expanded = expanded,
onExpandedChange = { expanded = it },
placeholder = { Text("What are you looking for?") },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
trailingIcon = {
if (expanded) {
IconButton(onClick = {
expanded = false
}) {
Icon(Icons.Default.Close, contentDescription = null)
}
}
},
query = query ?: "",
onQueryChange = {
query = it
}
)
The implementation for this InputField composable can then be slotted into the inputField argument for the SearchBar composable.
var expanded by remember { mutableStateOf(false) }
var query by remember { mutableStateOf<String?>(null) }
SearchBar(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onExpandedChange = {
expanded = it
},
inputField = {
SearchBarDefaults.InputField(
onSearch = { expanded = false },
expanded = expanded,
onExpandedChange = { expanded = it },
placeholder = { Text("What are you looking for?") },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
trailingIcon = {
if (expanded) {
IconButton(onClick = {
expanded = false
}) {
Icon(Icons.Default.Close, contentDescription = null)
}
}
},
query = query ?: "",
onQueryChange = {
query = it
}
)
}
)
At this point, we would be able to compose the SearchBar and see the floating component displayed within our UI.

All that remains for us to implement at this point is the content of the SearchBar, this is the content that is displayed when the SearchBar is in an expanded state. This argument utilise the ColumnScope, so any composables that are provided here will be stacked vertically. The expected form for this content area is a list of recommendations that can be selected by the user, so we’ll go ahead and compose a few ListItem composables that will each used to display a search recommendation to the user. When any of these items are clicked, the query will be updated to the selected value and the expanded state of the SearchBar reset.
var expanded by remember { mutableStateOf(false) }
var query by remember { mutableStateOf<String?>(null) }
SearchBar(
...
) {
listOf("Result 1", "Result 2", "Result 3", "Result 4").forEach { text ->
ListItem(
headlineContent = { Text(text) },
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
modifier = Modifier.clickable {
query = text
expanded = false
}.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp)
)
}
}
With this in place, we’ll now be able to see recommendations being shown beneath the floating search bar when it comes into focus.

With the above in place, we’ve been able to implement a floating search bar that displays search recommendations to the user. Using the Material3 SearchBar composable, it has been very little effort to implement such a composable that transitions between these two different states. Maybe you are already using the SearchBar in your app or have been looking for similar functionality, but either way, I’m looking forward to seeing more apps saving time from a wider support of components in Jetpack Compose!