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 una frase para mostrar resultados relevantes en tu app. Se recomienda cuando la búsqueda es el objetivo 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).

Plataforma de la API

Usa el elemento componible SearchBar 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, usa SearchBarDefaults.InputField, que permite personalizar lo siguiente:
    • query: Es el texto de la consulta que se mostrará en el campo de entrada.
    • onQueryChange: Lambda para controlar los cambios en la cadena de consulta.
  • expanded: Es un valor booleano que indica si la barra de búsqueda se expande para mostrar sugerencias o resultados filtrados.
  • onExpandedChange: Es una funció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 se conserve si la barra de búsqueda está expandida o colapsada 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 componibles secundarios.
    • traversalIndex se configura para especificar el orden en el que TalkBack lee la información de accesibilidad de cada par de grupo. TalkBack lee la información de accesibilidad en un par con un valor negativo, como -1, antes que un par con un valor positivo, como 1. Debido a que el valor es un número de punto flotante, puedes especificar un orden personalizado de muchos pares configurando valores entre -1.0 y 1.0 en cada uno de ellos.
  • 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 consulta 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 The expanded controla la visibilidad de la lista de sugerencias.
  • searchResults.forEach { result -> … } itera por la lista searchResults y crea un ListItem para cada resultado.
    • Cuando se hace clic en una ListItem, se actualiza la textFieldState, se contrae la barra de búsqueda y se completa la textField con el resultado de la búsqueda seleccionado.

Resultado

Se muestra una barra de búsqueda con la letra &quot;a&quot; escrita en 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 comienzo del campo de entrada, y un trailingIcon, que agrega un ícono de “más opciones” al final del campo de entrada. Aquí, puedes proporcionarle opciones de ordenamiento y filtrado al usuario.
  • onSearch = { … } llama a la 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 searchResults y muestra cada resultado como un ListItem.
  • Cada elemento ListItem componible muestra el texto del elemento, el 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 marcar el elemento como favorito.
  • Para obtener 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 una.
Figura 3: Se muestra una barra de búsqueda con sugerencias relevantes.

Recursos adicionales