Barra de búsqueda

Usa una barra de búsqueda para implementar la función de búsqueda. Una barra de búsqueda es un campo de búsqueda persistente que permite a los usuarios ingresar una palabra clave o frase para mostrar resultados relevantes dentro de tu app. Se recomienda cuando la búsqueda es el enfoque principal de tu app.

Se muestran dos barras de búsqueda. El de la izquierda solo tiene un campo de texto.
  La barra de búsqueda de la izquierda tiene un campo de texto y una sugerencia de búsqueda debajo.
Figura 1: Una barra de búsqueda básica (1) y una barra de búsqueda con una sugerencia (2).

Superficie de la API

Usa el elemento SearchBar componible para implementar barras de búsqueda. Entre los parámetros clave de este elemento componible, se incluyen los siguientes:

  • inputField: Define el campo de entrada de la barra de búsqueda. Por lo general, utiliza SearchBarDefaults.InputField, lo que permite personalizar lo siguiente:
    • query: Es el texto de la búsqueda que se mostrará en el campo de entrada.
    • onQueryChange: Es la función Lambda que controla los cambios en la cadena de búsqueda.
  • expanded: Es un valor booleano que indica si la barra de búsqueda se expandió para mostrar sugerencias o resultados filtrados.
  • onExpandedChange: Es una expresión lambda para controlar los cambios en el estado expandido del menú desplegable.

  • content: Es el contenido de esta barra de búsqueda para mostrar los resultados de la búsqueda debajo de inputField.

En este fragmento, se muestra una implementación básica de SearchBar con sugerencias:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleSearchBar(
    textFieldState: TextFieldState,
    onSearch: (String) -> Unit,
    searchResults: List<String>,
    modifier: Modifier = Modifier
) {
    // Controls expansion state of the search bar
    var expanded by rememberSaveable { mutableStateOf(false) }

    Box(
        modifier
            .fillMaxSize()
            .semantics { isTraversalGroup = true }
    ) {
        SearchBar(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .semantics { traversalIndex = 0f },
            inputField = {
                SearchBarDefaults.InputField(
                    query = textFieldState.text.toString(),
                    onQueryChange = { textFieldState.edit { replace(0, length, it) } },
                    onSearch = {
                        onSearch(textFieldState.text.toString())
                        expanded = false
                    },
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    placeholder = { Text("Search") }
                )
            },
            expanded = expanded,
            onExpandedChange = { expanded = it },
        ) {
            // Display search results in a scrollable column
            Column(Modifier.verticalScroll(rememberScrollState())) {
                searchResults.forEach { result ->
                    ListItem(
                        headlineContent = { Text(result) },
                        modifier = Modifier
                            .clickable {
                                textFieldState.edit { replace(0, length, result) }
                                expanded = false
                            }
                            .fillMaxWidth()
                    )
                }
            }
        }
    }
}

Puntos clave sobre el código

  • rememberSaveable garantiza que el estado de la barra de búsqueda (expandida o contraída) se conserve en los cambios de configuración. Escribe el valor recordado en el paquete savedInstanceState de la actividad de host antes de que se destruya la actividad durante un cambio de configuración.
  • El modificador semantics controla el orden de recorrido de TalkBack.
    • isTraversalGroup se establece para que Box agrupe todos sus elementos isTraversalGroup secundarios.
    • traversalIndex se configura para especificar el orden en el que TalkBack lee la información de accesibilidad de cada elemento secundario del grupo. TalkBack lee la información de accesibilidad en un elemento del mismo nivel con un valor negativo, como -1, antes de un elemento del mismo nivel con un valor positivo, como 1. Dado que el valor es un número de punto flotante, puedes especificar un orden personalizado de muchos pares estableciendo valores entre -1.0 y 1.0 en cada par.
  • El SearchBar contiene un inputField para la entrada del usuario y un Column para mostrar sugerencias de búsqueda.
    • SearchBarDefaults.InputField crea el campo de entrada y controla los cambios en la búsqueda del usuario.
    • onQueryChange controla la entrada de texto y actualiza el estado cada vez que cambia el texto en el campo de entrada.
    • El estado de The expanded controla la visibilidad de la lista de sugerencias.
  • searchResults.forEach { result -> … } itera a través de la lista searchResults y crea un ListItem para cada resultado.
    • Cuando se hace clic en un ListItem, se actualiza el textFieldState, se contrae la barra de búsqueda y se completa el textField con el resultado de la búsqueda seleccionado.

Resultado

Se muestra una barra de búsqueda con la letra &quot;a&quot; escrita dentro de ella. Debajo de la barra de búsqueda, se muestra una lista con seis sugerencias de búsqueda.
Figura 2: Una barra de búsqueda con sugerencias.

Barra de búsqueda con lista filtrada

En este ejemplo, se muestra un SearchBar que filtra una lista según la búsqueda del usuario:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomizableSearchBar(
    query: String,
    onQueryChange: (String) -> Unit,
    onSearch: (String) -> Unit,
    searchResults: List<String>,
    onResultClick: (String) -> Unit,
    // Customization options
    placeholder: @Composable () -> Unit = { Text("Search") },
    leadingIcon: @Composable (() -> Unit)? = { Icon(Icons.Default.Search, contentDescription = "Search") },
    trailingIcon: @Composable (() -> Unit)? = null,
    supportingContent: (@Composable (String) -> Unit)? = null,
    leadingContent: (@Composable () -> Unit)? = null,
    modifier: Modifier = Modifier
) {
    // Track expanded state of search bar
    var expanded by rememberSaveable { mutableStateOf(false) }

    Box(
        modifier
            .fillMaxSize()
            .semantics { isTraversalGroup = true }
    ) {
        SearchBar(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .semantics { traversalIndex = 0f },
            inputField = {
                // Customizable input field implementation
                SearchBarDefaults.InputField(
                    query = query,
                    onQueryChange = onQueryChange,
                    onSearch = {
                        onSearch(query)
                        expanded = false
                    },
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    placeholder = placeholder,
                    leadingIcon = leadingIcon,
                    trailingIcon = trailingIcon
                )
            },
            expanded = expanded,
            onExpandedChange = { expanded = it },
        ) {
            // Show search results in a lazy column for better performance
            LazyColumn {
                items(count = searchResults.size) { index ->
                    val resultText = searchResults[index]
                    ListItem(
                        headlineContent = { Text(resultText) },
                        supportingContent = supportingContent?.let { { it(resultText) } },
                        leadingContent = leadingContent,
                        colors = ListItemDefaults.colors(containerColor = Color.Transparent),
                        modifier = Modifier
                            .clickable {
                                onResultClick(resultText)
                                expanded = false
                            }
                            .fillMaxWidth()
                            .padding(horizontal = 16.dp, vertical = 4.dp)
                    )
                }
            }
        }
    }
}

Puntos clave sobre el código

  • Se llama a la función lambda onQueryChange cada vez que el usuario escribe o borra texto en la barra de búsqueda.
  • SearchBarDefaults.InputField contiene un leadingIcon, que agrega un ícono de búsqueda al principio del campo de entrada, y un trailingIcon, que agrega un ícono de "más opciones" al final del campo de entrada. Aquí puedes proporcionar opciones de ordenamiento y filtrado al usuario.
  • onSearch = { … } llama a la expresión lambda onSearch y contrae la barra de búsqueda cuando se envía la búsqueda.
  • Un LazyColumn controla de manera eficiente una cantidad potencialmente grande de resultados de la búsqueda. Itera en la lista de searchResults y muestra cada resultado como un ListItem.
  • Cada elemento ListItem componible muestra el texto del elemento, texto que muestra información adicional y un ícono de estrella como el leadingContent del elemento. En este ejemplo, se presenta una opción para agregar el elemento a favoritos.
  • Para ver la lógica de filtrado, consulta CustomizableSearchBarExample en el código fuente completo en GitHub.

Resultado

Se muestra una barra de búsqueda que contiene las palabras de la búsqueda de texto sugerida. Debajo de la barra de búsqueda, se muestra una lista de sugerencias de búsqueda, con un ícono de estrella junto a cada sugerencia.
Figura 3: Una barra de búsqueda con sugerencias pertinentes.

Recursos adicionales