Skip to content

Commit eb29ebd

Browse files
authored
Searchbar snippets refactor (#462)
* Refactor search bar examples * Add comments * Apply Spotless
1 parent 7251dec commit eb29ebd

File tree

1 file changed

+129
-74
lines changed
  • compose/snippets/src/main/java/com/example/compose/snippets/components

1 file changed

+129
-74
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/components/SearchBar.kt

Lines changed: 129 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
2626
import androidx.compose.foundation.layout.padding
2727
import androidx.compose.foundation.lazy.LazyColumn
2828
import androidx.compose.foundation.rememberScrollState
29+
import androidx.compose.foundation.text.input.TextFieldState
30+
import androidx.compose.foundation.text.input.rememberTextFieldState
2931
import androidx.compose.foundation.verticalScroll
3032
import androidx.compose.material.icons.Icons
3133
import androidx.compose.material.icons.filled.MoreVert
@@ -68,26 +70,32 @@ fun SearchBarExamples() {
6870
var currentExample by remember { mutableStateOf<String?>(null) }
6971

7072
when (currentExample) {
71-
"basic" -> SearchBarBasicFilterList()
72-
"advanced" -> AppSearchBar()
73+
"simple" -> SimpleSearchBarExample()
74+
"fancy" -> CustomizableSearchBarExample()
7375
else -> {
74-
Button(onClick = { currentExample = "basic" }) {
75-
Text("Basic search bar with filter")
76+
Button(onClick = { currentExample = "simple" }) {
77+
Text("Simple SearchBar")
7678
}
77-
Button(onClick = { currentExample = "advanced" }) {
78-
Text("Advanced search bar with filter")
79+
Button(onClick = { currentExample = "fancy" }) {
80+
Text("Customizable SearchBar")
7981
}
8082
}
8183
}
8284
}
8385
}
8486

87+
// [START android_compose_components_simple_searchbar]
8588
@OptIn(ExperimentalMaterial3Api::class)
86-
// [START android_compose_components_searchbarbasicfilterlist]
8789
@Composable
88-
fun SearchBarBasicFilterList(modifier: Modifier = Modifier) {
89-
var text by rememberSaveable { mutableStateOf("") }
90+
fun SimpleSearchBar(
91+
textFieldState: TextFieldState,
92+
onSearch: (String) -> Unit,
93+
searchResults: List<String>,
94+
modifier: Modifier = Modifier
95+
) {
96+
// Controls expansion state of the search bar
9097
var expanded by rememberSaveable { mutableStateOf(false) }
98+
9199
Box(
92100
modifier
93101
.fillMaxSize()
@@ -99,26 +107,28 @@ fun SearchBarBasicFilterList(modifier: Modifier = Modifier) {
99107
.semantics { traversalIndex = 0f },
100108
inputField = {
101109
SearchBarDefaults.InputField(
102-
query = text,
103-
onQueryChange = { text = it },
104-
onSearch = { expanded = false },
110+
query = textFieldState.text.toString(),
111+
onQueryChange = { textFieldState.edit { replace(0, length, it) } },
112+
onSearch = {
113+
onSearch(textFieldState.text.toString())
114+
expanded = false
115+
},
105116
expanded = expanded,
106117
onExpandedChange = { expanded = it },
107-
placeholder = { Text("Hinted search text") }
118+
placeholder = { Text("Search") }
108119
)
109120
},
110121
expanded = expanded,
111122
onExpandedChange = { expanded = it },
112123
) {
124+
// Display search results in a scrollable column
113125
Column(Modifier.verticalScroll(rememberScrollState())) {
114-
repeat(4) { index ->
115-
val resultText = "Suggestion $index"
126+
searchResults.forEach { result ->
116127
ListItem(
117-
headlineContent = { Text(resultText) },
118-
supportingContent = { Text("Additional info") },
128+
headlineContent = { Text(result) },
119129
modifier = Modifier
120130
.clickable {
121-
text = resultText
131+
textFieldState.edit { replace(0, length, result) }
122132
expanded = false
123133
}
124134
.fillMaxWidth()
@@ -128,27 +138,55 @@ fun SearchBarBasicFilterList(modifier: Modifier = Modifier) {
128138
}
129139
}
130140
}
131-
// [END android_compose_components_searchbarbasicfilterlist]
141+
// [END android_compose_components_simple_searchbar]
132142

133143
@Preview(showBackground = true)
134144
@Composable
135-
private fun SearchBarBasicFilterListPreview() {
136-
SearchBarBasicFilterList()
145+
private fun SimpleSearchBarExample() {
146+
// Create and remember the text field state
147+
val textFieldState = rememberTextFieldState()
148+
val items = listOf(
149+
"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb",
150+
"Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"
151+
)
152+
153+
// Filter items based on the current search text
154+
val filteredItems by remember {
155+
derivedStateOf {
156+
val searchText = textFieldState.text.toString()
157+
if (searchText.isEmpty()) {
158+
emptyList()
159+
} else {
160+
items.filter { it.contains(searchText, ignoreCase = true) }
161+
}
162+
}
163+
}
164+
165+
SimpleSearchBar(
166+
textFieldState = textFieldState,
167+
onSearch = { /* Handle search submission */ },
168+
searchResults = filteredItems
169+
)
137170
}
138171

139-
// [START android_compose_components_searchbarfilterlist]
172+
// [START android_compose_components_customizable_searchbar]
140173
@OptIn(ExperimentalMaterial3Api::class)
141174
@Composable
142-
fun SearchBarFilterList(
143-
list: List<String>,
175+
fun CustomizableSearchBar(
176+
query: String,
177+
onQueryChange: (String) -> Unit,
178+
onSearch: (String) -> Unit,
179+
searchResults: List<String>,
180+
onResultClick: (String) -> Unit,
181+
// Customization options
182+
placeholder: @Composable () -> Unit = { Text("Search") },
183+
leadingIcon: @Composable (() -> Unit)? = { Icon(Icons.Default.Search, contentDescription = "Search") },
184+
trailingIcon: @Composable (() -> Unit)? = null,
185+
supportingContent: (@Composable (String) -> Unit)? = null,
186+
leadingContent: (@Composable () -> Unit)? = null,
144187
modifier: Modifier = Modifier
145188
) {
146-
var text by rememberSaveable { mutableStateOf("") }
147-
val filteredList by remember {
148-
derivedStateOf {
149-
list.filter { it.lowercase().contains(text.lowercase()) }
150-
}
151-
}
189+
// Track expanded state of search bar
152190
var expanded by rememberSaveable { mutableStateOf(false) }
153191

154192
Box(
@@ -161,36 +199,36 @@ fun SearchBarFilterList(
161199
.align(Alignment.TopCenter)
162200
.semantics { traversalIndex = 0f },
163201
inputField = {
202+
// Customizable input field implementation
164203
SearchBarDefaults.InputField(
165-
query = text,
166-
onQueryChange = { text = it },
167-
onSearch = { expanded = false },
204+
query = query,
205+
onQueryChange = onQueryChange,
206+
onSearch = {
207+
onSearch(query)
208+
expanded = false
209+
},
168210
expanded = expanded,
169211
onExpandedChange = { expanded = it },
170-
placeholder = { Text("Hinted search text") },
171-
leadingIcon = { Icon(Icons.Default.Search, contentDescription = "Search") },
172-
trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = "More options") },
212+
placeholder = placeholder,
213+
leadingIcon = leadingIcon,
214+
trailingIcon = trailingIcon
173215
)
174216
},
175217
expanded = expanded,
176218
onExpandedChange = { expanded = it },
177219
) {
220+
// Show search results in a lazy column for better performance
178221
LazyColumn {
179-
items(count = filteredList.size) { index ->
180-
val resultText = filteredList[index]
222+
items(count = searchResults.size) { index ->
223+
val resultText = searchResults[index]
181224
ListItem(
182225
headlineContent = { Text(resultText) },
183-
supportingContent = { Text("Additional info") },
184-
leadingContent = {
185-
Icon(
186-
Icons.Filled.Star,
187-
contentDescription = "Starred item"
188-
)
189-
},
226+
supportingContent = supportingContent?.let { { it(resultText) } },
227+
leadingContent = leadingContent,
190228
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
191229
modifier = Modifier
192230
.clickable {
193-
text = resultText
231+
onResultClick(resultText)
194232
expanded = false
195233
}
196234
.fillMaxWidth()
@@ -199,10 +237,52 @@ fun SearchBarFilterList(
199237
}
200238
}
201239
}
240+
}
241+
}
242+
// [END android_compose_components_customizable_searchbar]
243+
244+
@Preview(showBackground = true)
245+
@Composable
246+
fun CustomizableSearchBarExample() {
247+
// Manage query state
248+
var query by rememberSaveable { mutableStateOf("") }
249+
val items = listOf(
250+
"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb",
251+
"Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop", "Marshmallow",
252+
"Nougat", "Oreo", "Pie"
253+
)
254+
255+
// Filter items based on query
256+
val filteredItems by remember {
257+
derivedStateOf {
258+
if (query.isEmpty()) {
259+
items
260+
} else {
261+
items.filter { it.contains(query, ignoreCase = true) }
262+
}
263+
}
264+
}
265+
266+
Column(modifier = Modifier.fillMaxSize()) {
267+
CustomizableSearchBar(
268+
query = query,
269+
onQueryChange = { query = it },
270+
onSearch = { /* Handle search submission */ },
271+
searchResults = filteredItems,
272+
onResultClick = { query = it },
273+
// Customize appearance with optional parameters
274+
placeholder = { Text("Search desserts") },
275+
leadingIcon = { Icon(Icons.Default.Search, contentDescription = "Search") },
276+
trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = "More options") },
277+
supportingContent = { Text("Android dessert") },
278+
leadingContent = { Icon(Icons.Filled.Star, contentDescription = "Starred item") }
279+
)
280+
281+
// Display the filtered list below the search bar
202282
LazyColumn(
203283
contentPadding = PaddingValues(
204284
start = 16.dp,
205-
top = 72.dp,
285+
top = 72.dp, // Provides space for the search bar
206286
end = 16.dp,
207287
bottom = 16.dp
208288
),
@@ -211,34 +291,9 @@ fun SearchBarFilterList(
211291
traversalIndex = 1f
212292
},
213293
) {
214-
items(count = filteredList.size) {
215-
Text(text = filteredList[it])
294+
items(count = filteredItems.size) {
295+
Text(text = filteredItems[it])
216296
}
217297
}
218298
}
219299
}
220-
// [END android_compose_components_searchbarfilterlist]
221-
222-
@Preview(showBackground = true)
223-
@Composable
224-
fun AppSearchBar(modifier: Modifier = Modifier) {
225-
SearchBarFilterList(
226-
list = listOf(
227-
"Cupcake",
228-
"Donut",
229-
"Eclair",
230-
"Froyo",
231-
"Gingerbread",
232-
"Honeycomb",
233-
"Ice Cream Sandwich",
234-
"Jelly Bean",
235-
"KitKat",
236-
"Lollipop",
237-
"Marshmallow",
238-
"Nougat",
239-
"Oreo",
240-
"Pie"
241-
),
242-
modifier
243-
)
244-
}

0 commit comments

Comments
 (0)