Best Website for Jetpack Compose App Development

Android Jetpack Compose

Stay ahead with the latest tools, trends, and best practices in Android Jetpack Compose App development

Search bar MDC 3 Jetpack Compose

Search bar MDC 3 Jetpack Compose  - Responsive Blogger Template
Search bar MDC 3 Jetpack Compose

Search bar in Android Jetpack Compose

Jetpack Compose provides a modern and powerful SearchBar component in Material 3 that makes it easy to build accessible and state-driven search experiences.

This tutorial explains:

  • How SearchBar works
  • How search suggestions are shown
  • State handling (query, expanded)
  • Accessibility with semantics
  • Best practices

What is a Search Bar?

A Search Bar allows users to:
  • Enter a search query
  • View suggestions or results
  • Select an item to autofill the search text
In Compose, UI reacts to state, not events.

Key Concepts

ConceptPurpose
SearchBarContainer for search UI
InputFieldHandles user text input
expandedControls visibility of suggestions
rememberSaveablePreserves state on rotation
ColumnDisplays search suggestions
ListItemEach search result
semanticsAccessibility (TalkBack)

Required Dependencies

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.09.00"))
    implementation("androidx.compose.material3:material3")
}

Basic Search Bar Structure

package com.example.composeapp

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchBarExample() {
    var query by rememberSaveable { mutableStateOf("") }
    var expanded by rememberSaveable { mutableStateOf(false) }

    val searchResults = listOf(
        "Android",
        "Jetpack Compose",
        "Kotlin",
        "Material Design",
        "Room Database"
    ).filter {
        it.contains(query, ignoreCase = true)
    }

    Box(
        modifier = Modifier
            .fillMaxWidth()
            .semantics { isTraversalGroup = true }
    ) {
        SearchBar(
            inputField = {
                SearchBarDefaults.InputField(
                    query = query,
                    onQueryChange = { query = it },
                    onSearch = { expanded = false },
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    placeholder = { Text("Search topics") }
                )
            },
            expanded = expanded,
            onExpandedChange = { expanded = it },
            modifier = Modifier
                .fillMaxWidth()
                .semantics { traversalIndex = 0f }
        ) {
            SearchSuggestions(
                searchResults = searchResults,
                onResultClick = { result ->
                    query = result
                    expanded = false
                }
            )
        }
    }
}

Search Suggestions UI


@Composable
fun SearchSuggestions(
    searchResults: List,
    onResultClick: (String) -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .semantics { traversalIndex = 1f }
    ) {
        searchResults.forEach { result ->
            ListItem(
                headlineContent = { Text(result) },
                modifier = Modifier.clickable {
                    onResultClick(result)
                }
            )
        }
    }
}

OUTPUT:

Basic Search Bar in Jetpack Compose

Basic Search screenshot Bar in Jetpack Compose

How Everything Works

1. Input Field

SearchBarDefaults.InputField(...)
✔ Creates the text input
✔ Automatically manages keyboard & focus
✔ Calls onQueryChange whenever user types

2. Query State

var query by rememberSaveable { mutableStateOf("") }
  • Stores user input
  • Survives screen rotation
  • Updates UI automatically

3. Expanded State

var expanded by rememberSaveable { mutableStateOf(false) }
Controls:
  • When suggestions are visible
  • When search bar is open/closed

4. Search Results Logic

val searchResults = list.filter {
    it.contains(query, ignoreCase = true)
}
  • Filters data dynamically
  • UI updates instantly on typing

5. Showing Suggestions

searchResults.forEach { result ->
    ListItem(...)
}
  • Iterates through results
  • Creates a clickable suggestion list

6. Handling Click on Suggestion

query = result
expanded = false
✔ Fills text field
✔ Collapses search bar
✔ Improves UX

❓ FREQUENTLY ASKED QUESTIONS (FAQ)

FAQ 1: Does SearchBar automatically manage keyboard?

Yes. It handles focus and IME actions internally.

FAQ 2: Can I show recent searches?

Yes. Store recent searches in a list and display them when query is empty.

FAQ 3: Does SearchBar support dark mode?

Yes. It follows Material Theme colors automatically.

FAQ 4: Can SearchBar work inside LazyColumn?

Yes, but wrap it properly to avoid recomposition issues.

FAQ 5: Should SearchBar be placed inside Scaffold?

Recommended, especially inside topBar.

FAQ 6: How to debounce search input?

Use:
  • LaunchedEffect
  • snapshotFlow
  • Flow debounce()

FAQ 7: Is SearchBar better than custom search UI?

Yes, because:
  • Consistent UX
  • Accessibility support
  • Less boilerplate

FAQ 8: Can I disable suggestions?

Yes. Keep expanded = false.

FAQ 9: Is SearchBar backward compatible?

It works on all Compose-supported Android versions.

FAQ 10: Is SearchBar suitable for large datasets?

Yes, but filtering should be done in:

❌ Common Mistakes in Jetpack Compose SearchBar

(Material Design 3)


1. Using

remember

Instead of

rememberSaveable

❌ Wrong

var query by remember { mutableStateOf("") }

⚠️ Problem
State resets on screen rotation.

✅ Correct


var query by rememberSaveable { mutableStateOf("") }

2. Not Collapsing SearchBar After Selection

❌ Wrong


query = result

⚠️ Problem
SearchBar stays open → poor UX.

✅ Correct


query = result
expanded = false

3. Performing Heavy Logic Inside Composable

❌ Wrong


val results = hugeList.filter { expensiveOperation(it) }

⚠️ Problem
Causes lag and recomposition issues.

✅ Correct

  • Move logic to ViewModel
  • Repository layer

4. Forgetting to Handle Empty Query Case

❌ Wrong


searchResults.forEach { ... }

⚠️ Problem
Suggestions appear even when query is empty.

✅ Correct


if (query.isNotEmpty()) {
    searchResults.forEach { ... }
}

5. Not Using expanded State Properly

❌ Wrong


SearchBar(expanded = true)

⚠️ Problem
SearchBar always open.

✅ Correct


var expanded by rememberSaveable { mutableStateOf(false) }

6. Ignoring onSearch Keyboard Action

❌ Wrong


onSearch = {}

⚠️ Problem
Keyboard search button does nothing.

✅ Correct


onSearch = {
    expanded = false
}

7. Duplicating Recent Searches

❌ Wrong


recentSearches.add(query)

⚠️ Problem
Same item appears multiple times.

✅ Correct


if (!recentSearches.contains(query)) {
    recentSearches.add(0, query)
}

8. Storing UI State in ViewModel Incorrectly

❌ Wrong


val expanded = mutableStateOf(false)

⚠️ Problem
UI state tied incorrectly to business logic.

✅ Correct
Keep UI-only state inside Composable.


9. Missing Accessibility Semantics

❌ Wrong


SearchBar(...)

⚠️ Problem
Poor TalkBack experience.

✅ Correct


Modifier.semantics { isTraversalGroup = true 

10. Incorrect TalkBack Traversal Order

❌ Wrong


traversalIndex = 1f

⚠️ Problem
Suggestions read before input field.

✅ Correct

  • InputField → -1f
  • Suggestions → 1f

11. Filtering List on Every Recomposition

❌ Wrong


val results = list.filter { ... }

⚠️ Problem
Unnecessary recomputations.

✅ Correct


val results by remember(query) {
    mutableStateOf(...)
}

12. Not Limiting Recent Search Size

❌ Wrong


recentSearches.add(query)

⚠️ Problem
Unbounded list growth.

✅ Correct


if (recentSearches.size >= 5) {
    recentSearches.removeLast()
}

13. Hardcoding Strings

❌ Wrong


Text("Search here")

⚠️ Problem
No localization support.

✅ Correct


Text(stringResource(R.string.search_hint))

14. Not Handling Focus Properly

❌ Problem
SearchBar opens but keyboard doesn’t appear.

✅ Fix
Use default SearchBarDefaults.InputField to manage focus automatically.


15. Forgetting to Clear Query State

❌ Problem
Old query remains when reopening search.

✅ Correct

query = ""
expanded = false

16. Using SearchBar for Non-Search UI

❌ Wrong
Using SearchBar as a normal text field.

✅ Correct
Use TextField for forms and SearchBar only for search.


17. Not Handling Dark Mode

❌ Wrong
Custom colors ignoring theme.

✅ Correct
Use MaterialTheme.colorScheme.


18. Not Debouncing API Calls

❌ Wrong


onQueryChange { callApi(it) }

⚠️ Problem
Too many API calls.

✅ Correct
Use Flow + debounce.


19. Showing Empty UI When No Results

❌ Wrong
Nothing appears.

✅ Correct


Text("No results found")

20. Forgetting to Close Keyboard

❌ Problem
Keyboard stays open after selection.

✅ Fix
Collapse SearchBar or manage focus.


📌 Tip: These mistakes are frequently asked in interviews and occur in real production apps. Avoiding them ensures better UX, performance, and accessibility.

SkillDedication

— Python High Level Programming Language- Expert-Written Tutorials, Projects, and Tools—

Special Message

Welcome To Coding Bihar👨‍🏫