Discovering Material 3 for Android
This series of articles is designed to help you explore the latest addition to Material 3 for Android. In this series, we will take a closer look at the different components and features of Material 3, and explore how they can be used to create modern and user-friendly Android apps.
Contents
Search for data within an application (and inside a list of items) is a powerful feature that makes it easy for users to find what they are looking for. In this article, we will discuss the newly added experimental Material 3 SearchBar component, its benefits, and how to use it.
Two Search Configurations
We have two types of configuration available to us:
- SearchBar, acting as a “full screen” search view
- DockedSearchBar, acting as a “floating” search view
Both configurations allow us to perform queries and show results in a “dropdown” style.
⚠️ As mentioned in previous articles about
DatePicker, it is important to note that theSearchBarandDockedSearchBarcomponents in Jetpack Compose are still experimental and were recently released. This means that it may not be suitable for production use and could contain bugs or other issues.
Material 3 DockedSearchBar
A DockedSearchBar displays search results in a bounded table below the input field. It is meant to be an alternative to SearchBar when expanding to full-screen size.
API for the DockedSearchBar looks like this:
@ExperimentalMaterial3Api
@Composable
fun DockedSearchBar(
query: String,
onQueryChange: (String) -> Unit,
onSearch: (String) -> Unit,
active: Boolean,
onActiveChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
shape: Shape = SearchBarDefaults.dockedShape,
colors: SearchBarColors = SearchBarDefaults.colors(),
tonalElevation: Dp = SearchBarDefaults.Elevation,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable ColumnScope.() -> Unit,
)
To implement a DockedSearchBar, we will need to remember two values in the composition process:
text: Stringthe value of the TextField in the search boxactive: Booleanare we showing or not results in the SearchBar?
var text by rememberSaveable { mutableStateOf("") }
var active by rememberSaveable { mutableStateOf(false) }
Jetpack Compose provides a nice way to use a “floating box” using zIndex() a nice workaround to ensure focus in the search TextField:
Box(Modifier.fillMaxSize()) {
// Talkback focus order sorts based on x and y position before considering z-index
Box(Modifier
.semantics { isContainer = true }
.zIndex(1f)
.fillMaxWidth()) {
DockedSearchBar(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 8.dp),
query = text,
onQueryChange = { text = it },
onSearch = { active = false },
active = active,
onActiveChange = { active = it },
placeholder = { Text("Hinted search text") },
leadingIcon = { Icon(Icons.Rounded.Search, contentDescription = null) },
trailingIcon = { Icon(Icons.Rounded.MoreVert, contentDescription = null) },
) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(4) { idx ->
val resultText = "Suggestion $idx"
ListItem(
modifier = Modifier.clickable {
text = resultText
active = false
},
headlineContent = {
Text(
text = resultText,
style = MaterialTheme.typography.titleMedium
)
},
supportingContent = {
Text(
text = "Additional info",
style = MaterialTheme.typography.titleMedium
)
},
leadingContent = { Icon(Icons.Rounded.Star, contentDescription = null) },
)
}
}
}
}
}
Material 3 SearchBar
A SearchBar displays search results in a bounded table below the input field. It is meant to be a full-screen search view.
API for SearchBar is similar to DockedSearchBar, but with an additional windowInsets attribute:
@ExperimentalMaterial3Api
@Composable
fun SearchBar(
query: String,
onQueryChange: (String) -> Unit,
onSearch: (String) -> Unit,
active: Boolean,
onActiveChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
shape: Shape = SearchBarDefaults.inputFieldShape,
colors: SearchBarColors = SearchBarDefaults.colors(),
tonalElevation: Dp = SearchBarDefaults.Elevation,
windowInsets: WindowInsets = SearchBarDefaults.windowInsets,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable ColumnScope.() -> Unit,
)
To use SearchBar take the sample above and just change DockedSearchBar to SearchBar and enjoy!
Should I use DockedSearchBar or SearchBar?
Layouts should adapt to fit different screen sizes. On mobile devices, the search view should be a full-screen modal, while on a larger screen, the search view should be a modal connected to the search bar and scale with the search bar width.
The rule of thumb is: use a SearchBar on mobile devices, and switch to a modal search view (DockedSearchBar) on larger screens.
Benefits
One of the benefits of the SearchBar component in Jetpack Compose is that it automatically manages nice animations. When the user taps on the search field, the leading icon smoothly transitions to the left and the input field expands to fill the space. When the user clears the search field, the trailing icon smoothly transitions to the right and the input field contracts to its original size.