8. Application Hockey Version 3¶
Différentes versions de PlayerListWithSearch¶
Version précédente¶
@Composable
fun PlayerListWithSearch(modifier: Modifier = Modifier) {
var nameSearch by rememberSaveable { mutableStateOf("") }
var numberSearch: Int? by rememberSaveable { mutableStateOf(null) }
Column(modifier = modifier) {
Column(modifier = Modifier.padding(top = 20.dp, bottom = 20.dp)) {
TextField(
value = nameSearch,
label = { Text(text = "Nom") },
onValueChange = { nameSearch = it },
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
TextField(
value = (numberSearch ?: "").toString(),
label = { Text(text = "Numéro") },
onValueChange = {
numberSearch = if (it.isEmpty()) null else it.toIntOrNull() ?: numberSearch
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
}
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(getPlayers(nameSearch, numberSearch)) {
HockeyPlayerCard(player = it)
}
}
}
}
Description générale¶
Ce code définit une fonction composable nommée PlayerListWithSearch qui crée une interface utilisateur pour afficher
une liste de joueurs de hockey avec une fonctionnalité de recherche. L’interface comprend deux champs de texte pour la
recherche (un pour le nom et un pour le numéro) et une liste déroulante des joueurs correspondant aux critères de
recherche.
Description détaillée¶
1- Déclaration de la fonction :
- C’est une fonction composable qui accepte un
modifieroptionnel comme paramètre.
2- Variables d’état :
var nameSearch by rememberSaveable { mutableStateOf("") }
var numberSearch: Int? by rememberSaveable { mutableStateOf(null) }
nameSearch: Une chaîne de caractères pour stocker la recherche par nom.numberSearch: Un entier nullable pour stocker la recherche par numéro.- Ces variables utilisent
rememberSaveablepour conserver leur état même après une reconfiguration.
3- Structure principale :
- Utilise un
Columncomme conteneur principal avec le modificateur passé en paramètre.
4- Champs de recherche :
- Un
Columninterne avec un padding pour contenir les champs de recherche.
5- Champ de recherche par nom :
TextField(
value = nameSearch,
label = { Text(text = "Nom") },
onValueChange = { nameSearch = it },
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
- Un
TextFieldpour la recherche par nom. - La valeur est liée à
nameSearch. - Le label affiche “Nom”.
onValueChangemet à journameSearchavec la nouvelle valeur.
6- Champ de recherche par numéro :
TextField(
value = (numberSearch ?: "").toString(),
label = { Text(text = "Numéro") },
onValueChange = {
numberSearch = if (it.isEmpty()) null else it.toIntOrNull() ?: numberSearch
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
- Un
TextFieldpour la recherche par numéro. - La valeur affichée est la conversion en chaîne de
numberSearch(ou une chaîne vide si null). - Le label affiche “Numéro”.
onValueChangetente de convertir l’entrée en entier, conservant la valeur précédente si la conversion échoue.
7- Liste des joueurs :
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(getPlayers(nameSearch, numberSearch)) {
HockeyPlayerCard(player = it)
}
}
- Utilise un
LazyColumnpour afficher efficacement une liste potentiellement longue de joueurs. getPlayers(nameSearch, numberSearch)est appelé pour obtenir la liste filtrée des joueurs.- Chaque joueur est affiché en utilisant un composant
HockeyPlayerCard.
Ce code crée une interface utilisateur interactive permettant aux utilisateurs de rechercher des joueurs de hockey par nom et numéro, avec une mise à jour dynamique de la liste affichée en fonction des critères de recherche.
Nouvelle version¶
@Composable
fun PlayerListWithSearch(modifier: Modifier = Modifier) {
var nameSearch by rememberSaveable { mutableStateOf("") }
var numberSearch: Int? by rememberSaveable { mutableStateOf(null) }
Column(modifier = modifier) {
SearchTextFields(
nameSearch = nameSearch,
onNameChange = { nameSearch = it },
numberSearch = numberSearch,
onNumberChange =
{ numberSearch = if (it.isEmpty()) null else it.toIntOrNull() ?: numberSearch })
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(getPlayers(nameSearch, numberSearch)) {
HockeyPlayerCard(player = it)
}
}
}
}
@Composable
private fun SearchTextFields(
nameSearch: String,
onNameChange: (String) -> Unit,
numberSearch: Int?,
onNumberChange: (String) -> Unit,
) {
Column(modifier = Modifier.padding(top = 20.dp, bottom = 20.dp)) {
TextField(
value = nameSearch,
label = { Text(text = "Nom") },
onValueChange = onNameChange,
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
TextField(
value = (numberSearch ?: "").toString(),
label = { Text(text = "Numéro") },
onValueChange = onNumberChange,
modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, end = 20.dp)
)
}
}
Cette refactorisation illustre bien comment on peut améliorer la structure et la réutilisabilité du code en Jetpack Compose. Voici une explication détaillée de la transition entre les deux versions, en mettant l’accent sur la gestion des variables d’état et des fonctions lambda :
-
Création du nouveau composable
SearchTextFields:- Un nouveau composable privé a été créé pour encapsuler la logique des champs de recherche.
- Cela améliore la lisibilité et la réutilisabilité du code.
-
Paramètres du nouveau composable :
nameSearch: String: La valeur actuelle de la recherche par nom.onNameChange: (String) -> Unit: Une fonction lambda pour gérer les changements de nom.numberSearch: Int?: La valeur actuelle de la recherche par numéro.onNumberChange: (String) -> Unit: Une fonction lambda pour gérer les changements de numéro.
-
Passage des variables d’état :
- Dans
PlayerListWithSearch, les variables d’étatnameSearchetnumberSearchsont passées directement àSearchTextFields. - Cela permet à
SearchTextFieldsd’utiliser ces valeurs sans les posséder ou les modifier directement.
- Dans
-
Passage des fonctions lambda :
- Pour
onNameChange, une simple lambda est passée :{ nameSearch = it }. Cette lambda met à jour directement la variable d’étatnameSearch. - Pour
onNumberChange, la logique de conversion est passée en tant que lambda : Cette lambda gère la conversion de la chaîne en entier et la mise à jour denumberSearch.
- Pour
-
Utilisation dans
SearchTextFields:- Les
TextFieldutilisent maintenant les paramètres passés au lieu d’accéder directement aux variables d’état. value = nameSearchetvalue = (numberSearch ?: "").toString()utilisent les valeurs passées.onValueChange = onNameChangeetonValueChange = onNumberChangeutilisent les lambdas passées.
- Les
-
Avantages de cette approche :
- Séparation des préoccupations :
SearchTextFieldsne gère que l’affichage et la collecte des entrées. - État élevé (State hoisting) : L’état reste dans le composant parent, rendant
SearchTextFieldsplus flexible et réutilisable. - Testabilité améliorée : Il est plus facile de tester
SearchTextFieldsindépendamment.
- Séparation des préoccupations :
-
Implications pour la gestion de l’état :
- L’état (
nameSearchetnumberSearch) est toujours géré dansPlayerListWithSearch. SearchTextFieldsdevient un composant “sans état” (stateless), ne faisant que refléter l’état qui lui est passé.
- L’état (
Cette refactorisation est un excellent exemple de la façon dont on peut appliquer le principe de “state hoisting” en Jetpack Compose, en élevant l’état et les fonctions de modification d’état vers le composant parent. Cela rend le code plus modulaire, plus facile à maintenir et à tester, tout en conservant une séparation claire des responsabilités entre les composants.