Barre de recherche

Utilisez une barre de recherche pour implémenter la fonctionnalité de recherche. Une barre de recherche est un champ de recherche permanent qui permet aux utilisateurs de saisir un mot clé ou une expression pour afficher les résultats pertinents dans votre application. Elle est recommandée lorsque la recherche est l'objectif principal de votre application.

Deux barres de recherche s'affichent. Celui de gauche ne comporte qu'un champ de texte.
  La barre de recherche située à gauche comporte un champ de texte et une suggestion de recherche en dessous.
Figure 1. Barre de recherche de base (1) et barre de recherche avec suggestion (2).

Surface d'API

Utilisez le composable SearchBar pour implémenter des barres de recherche. Voici les principaux paramètres de ce composable :

  • inputField : définit le champ de saisie de la barre de recherche. Il utilise généralement SearchBarDefaults.InputField, qui permet de personnaliser les éléments suivants :
    • query : texte de la requête à afficher dans le champ de saisie.
    • onQueryChange : Lambda pour gérer les modifications apportées à la chaîne de requête.
  • expanded : valeur booléenne indiquant si la barre de recherche est développée pour afficher des suggestions ou des résultats filtrés.
  • onExpandedChange : lambda pour gérer les modifications de l'état développé du menu déroulant.

  • content : contenu de cette barre de recherche pour afficher les résultats de recherche sous inputField.

Cet extrait montre une implémentation de base de SearchBar avec des suggestions :

@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()
                    )
                }
            }
        }
    }
}

Points clés concernant le code

  • rememberSaveable garantit que l'état de la barre de recherche (développée ou réduite) est conservé lors des changements de configuration. Il écrit la valeur mémorisée dans le bundle savedInstanceState de l'activité hôte avant que l'activité ne soit détruite lors d'une modification de la configuration.
  • Le modificateur semantics contrôle l'ordre de parcours TalkBack.
    • isTraversalGroup est défini pour Box afin de regrouper tous ses composables enfants.
    • traversalIndex est défini pour spécifier l'ordre dans lequel TalkBack lit les informations sur l'accessibilité de chaque pair du groupe. TalkBack lit les informations d'accessibilité sur un pair avec une valeur négative, telle que -1, avant un pair avec une valeur positive, telle que 1. Étant donné que la valeur est un float, vous pouvez spécifier un ordre personnalisé pour de nombreux pairs en définissant des valeurs entre -1.0 et 1.0 sur chaque pair.
  • SearchBar contient un inputField pour la saisie utilisateur et un Column pour afficher les suggestions de recherche.
    • SearchBarDefaults.InputField crée le champ de saisie et gère les modifications apportées à la requête utilisateur.
    • onQueryChange gère la saisie de texte et met à jour l'état chaque fois que le texte du champ de saisie change.
    • L'état The expanded contrôle la visibilité de la liste de suggestions.
  • searchResults.forEach { result -> … } parcourt la liste searchResults et crée un ListItem pour chaque résultat.
    • Lorsqu'un ListItem est sélectionné, le textFieldState est mis à jour, la barre de recherche est réduite et le textField est rempli avec le résultat de recherche sélectionné.

Résultat

Une barre de recherche avec la lettre &quot;a&quot; saisie à l&#39;intérieur. Une liste de six suggestions de recherche s&#39;affiche sous la barre de recherche.
Figure 2. Barre de recherche avec des suggestions affichées.

Barre de recherche avec liste filtrée

Cet exemple montre un SearchBar qui filtre une liste en fonction de la requête de recherche de l'utilisateur :

@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)
                    )
                }
            }
        }
    }
}

Points clés concernant le code

  • La fonction lambda onQueryChange est appelée chaque fois que l'utilisateur saisit ou supprime du texte dans la barre de recherche.
  • SearchBarDefaults.InputField contient un leadingIcon, qui ajoute une icône de recherche au début du champ de saisie, et un trailingIcon, qui ajoute une icône "Plus d'options" à la fin du champ de saisie. Ici, vous pouvez fournir des options de tri et de filtrage à l'utilisateur.
  • onSearch = { … } appelle le lambda onSearch et réduit la barre de recherche lorsque la recherche est envoyée.
  • Un LazyColumn gère efficacement un nombre potentiellement important de résultats de recherche. Il parcourt la liste searchResults et affiche chaque résultat sous la forme d'un ListItem.
  • Chaque composable ListItem affiche le texte de l'élément, le texte affichant des informations supplémentaires et une icône en forme d'étoile en tant que leadingContent de l'élément. Dans cet exemple, une option permettant d'ajouter l'élément aux favoris est présentée.
  • Pour la logique de filtrage, consultez CustomizableSearchBarExample dans le code source complet sur GitHub.

Résultat

Une barre de recherche contenant les mots &quot;recherche de texte suggéré&quot; est affichée. Sous la barre de recherche, une liste de suggestions de recherche s&#39;affiche, avec une icône en forme d&#39;étoile à côté de chaque suggestion.
Figure 3. Barre de recherche affichant des suggestions pertinentes.

Ressources supplémentaires