
Le paramètre key
peut être trouvé dans presque tous les constructeurs de widgets, mais ce paramètre est rarement utilisé dans le développement. Keys
conservent leur état lors du déplacement des widgets dans l'arborescence des widgets. En pratique, cela signifie qu'ils peuvent être utiles pour enregistrer l'emplacement de défilement de l'utilisateur ou enregistrer l'état lorsque la collection change.
Cet article est adapté de la vidéo suivante. Si vous préférez écouter / regarder plutôt que lire, la vidéo vous fournira le même matériel.
La plupart du temps ... vous n'avez pas besoin de keys
. En général, il n'y a aucun mal à les ajouter, mais ce n'est pas non plus nécessaire, car ils ont simplement lieu en tant que nouveau mot clé ou déclaration de type des deux côtés d'une nouvelle variable (je parle de vous, Map<Foo, Bar> aMap = Map<Foo, Bar>()
).
Mais si vous constatez que vous ajoutez, supprimez ou réorganisez des widgets dans la collection qui contiennent un certain état et sont du même type, alors vous devriez faire attention aux keys
!
Pour démontrer pourquoi vous avez besoin de keys
lors du changement d'une collection de widgets, j'ai écrit une application extrêmement simple avec deux widgets colorés qui changent de place lorsqu'un bouton est cliqué:

Dans cette version de l'application, j'ai deux widgets sans état de couleur aléatoire ( StatelessWidget
) dans Row
et le widget PositionedTiles avec état ( StatefulWidget
) pour y stocker l'ordre des widgets de couleur. Lorsque je clique sur le bouton FloatingActionButton
en bas, les widgets de couleur changent correctement leur place dans la liste:
void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> { List<Widget> tiles = [ StatelessColorfulTile(), StatelessColorfulTile(), ]; @override Widget build(BuildContext context) { return Scaffold( body: Row(children: tiles), floatingActionButton: FloatingActionButton( child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles), ); } swapTiles() { setState(() { tiles.insert(1, tiles.removeAt(0)); }); } } class StatelessColorfulTile extends StatelessWidget { Color myColor = UniqueColorGenerator.getColor(); @override Widget build(BuildContext context) { return Container( color: myColor, child: Padding(padding: EdgeInsets.all(70.0))); } }
Mais si nous ajoutons un état à nos widgets de couleurs (les transformons en StatefulWidget
) et y stockons des couleurs, alors lorsque nous cliquons sur le bouton, il semble que rien ne se passe:

List<Widget> tiles = [ StatefulColorfulTile(), StatefulColorfulTile(), ]; ... class StatefulColorfulTile extends StatefulWidget { @override ColorfulTileState createState() => ColorfulTileState(); } class ColorfulTileState extends State<ColorfulTile> { Color myColor; @override void initState() { super.initState(); myColor = UniqueColorGenerator.getColor(); } @override Widget build(BuildContext context) { return Container( color: myColor, child: Padding( padding: EdgeInsets.all(70.0), )); } }
A titre d'explication: le code ci-dessus est bogué en ce qu'il n'affiche pas l'échange de couleurs lorsque l'utilisateur clique sur le bouton. Pour corriger cette erreur, vous devez ajouter le paramètre key
aux widgets StatefulWidget
colorés, puis les widgets seront échangés comme nous le souhaitons:

List<Widget> tiles = [ StatefulColorfulTile(key: UniqueKey()),
Mais cela n'est nécessaire que si vous avez des widgets avec état dans la sous-arborescence que vous modifiez. Si la sous-arborescence entière du widget de votre collection n'a pas d'état, aucune clé n'est nécessaire.
Et voilà! Dans l'ensemble, tout ce que vous devez savoir pour utiliser les keys
dans Flutter
. Mais si vous voulez aller un peu plus loin dans ce qui se passe ...
Comprendre pourquoi les keys
parfois nécessaires
Vous êtes toujours là, non? Eh bien, rapprochez-vous pour découvrir la vraie nature des arbres d'éléments et des widgets pour devenir un Flutter Mage! Wahahaha! Haha Haha Désolée.
Comme vous le savez, à l'intérieur de chaque widget, Flutter crée l'élément correspondant. Tout comme Flutter crée une arborescence de widgets, il crée également une arborescence d'éléments (ElementTree). ElementTree est extrêmement simple, il ne contient que les informations de type de chaque widget et un lien vers les éléments enfants. Vous pouvez penser à ElementTree comme le squelette de votre application Flutter. Il montre la structure de votre application, mais toutes les informations supplémentaires peuvent être trouvées sur le lien vers le widget source.
Le widget de ligne dans l'exemple ci-dessus contient un ensemble d'emplacements ordonnés pour chacun de ses enfants. Lorsque nous réorganisons les widgets de couleurs dans Row, Flutter parcourt ElementTree pour vérifier si la structure squelette de l'application est la même.

La validation commence par un RowElement, puis passe aux éléments enfants. ElementTree vérifie que le nouveau widget a le même type et la même key
que l'ancien, et si c'est le cas, l'élément met à jour son lien vers le nouveau widget. Dans la version sans état du code, les widgets n'ont pas de key
, donc Flutter vérifie simplement uniquement le type. (S'il y a trop d'informations à la fois, consultez le graphique animé ci-dessus.)
Sous ElementTree pour les widgets d'état, l'aspect est un peu différent. Il existe des widgets et des éléments, comme précédemment, mais il existe également quelques objets d'état pour les widgets, et les informations de couleur y sont stockées, et non dans les widgets eux-mêmes.

Dans le cas de widgets StatefulWidget
colorés sans key
, lorsque je change l'ordre des deux widgets, Flutter fait le tour d'ElementTree, vérifie le type de RowWidget et met à jour le lien. L'élément widget couleur vérifie ensuite que le widget correspondant est du même type et met à jour le lien. La même chose se produit avec le deuxième widget. Étant donné que Flutter utilise ElementTree et son état correspondant pour déterminer quoi afficher réellement sur votre appareil, de notre point de vue, il semble que les widgets n'ont pas été échangés!

Dans la version fixe du code en widgets colorés avec état dans le constructeur, j'ai défini la propriété key
. Maintenant, si nous modifions les widgets dans Row
, alors par type, ils correspondront comme avant, mais les valeurs de key
pour le widget de couleur et pour l'élément correspondant dans ElementTree seront différentes. Cela provoque Flutter pour désactiver ces éléments des widgets de couleur et supprimer les liens vers eux dans ElementTree, à partir du premier, qui ne correspond pas à la key
.

Flutter recherche ensuite les widgets de l'élément Row
dans ElementTree avec la key
correspondante. S'il correspond, il ajoute un lien vers l'élément widget. Flutter fait pour chaque enfant sans lien. Maintenant Flutter affichera ce que nous attendons, les widgets de couleur changeront de place lorsque je clique sur le bouton.
Ainsi, les keys
sont utiles si vous modifiez l'ordre ou le nombre de widgets avec l'état dans la collection. Dans cet exemple, j'ai enregistré la couleur. Cependant, souvent, la condition n'est pas si évidente. Lecture d'une animation, affichage des entrées de l'utilisateur et défilement d'un emplacement - tout a un état.
Quand dois-je utiliser des keys
?
Réponse courte: si vous devez ajouter des keys
à l'application, vous devez les ajouter en haut de la sous-arborescence du widget avec l'état que vous souhaitez enregistrer.
Une erreur courante que j'ai vue est que les gens pensent qu'ils doivent définir la key
uniquement pour le premier widget avec état, mais il y a des nuances. Ne me croyez pas? Pour montrer dans quels problèmes nous pourrions être, j'ai enveloppé mes widgets de couleur dans des widgets de Padding
, tout en laissant les clés des widgets de couleur.
void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> {
Maintenant, au simple toucher d'un bouton, les widgets obtiennent des couleurs complètement aléatoires!

Voici à quoi ressemblent l'arborescence des widgets et ElementTree avec les widgets Padding
ajoutés:

Lorsque nous modifions les positions des widgets enfants, l'algorithme de correspondance entre les éléments et les widgets ressemble à un niveau dans l'arborescence des éléments. Dans le diagramme, les enfants des enfants sont obscurcis afin que rien ne détourne l'attention du premier niveau. À ce niveau, tout correspond correctement.

Au deuxième niveau, Flutter remarque que la key
élément couleur ne correspond pas à la key
widget, il désactive donc cet élément, le jette et supprime tous les liens vers celui-ci. keys
nous utilisons dans cet exemple sont LocalKeys
. Cela signifie que lors de la mise en correspondance d'un widget avec des éléments, Flutter recherche des keys
uniquement à un certain niveau de l'arborescence.
Puisqu'il ne peut pas trouver l'élément de widget de couleur à ce niveau avec la key
correspondante, il en crée un nouveau et initialise un nouvel état, rendant le widget orange dans ce cas!

Si nous définissons des keys
pour les widgets de Padding
:
void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> { List<Widget> tiles = [ Padding(
Flutter remarque le problème et met à jour les liens correctement, comme c'était le cas dans notre exemple précédent. L'ordre dans l'univers est rétabli.

Quel type de Key
dois-je utiliser?
Les API Flutter nous ont donné le choix entre plusieurs classes de Key
. Le type de key
vous devez utiliser dépend de la caractéristique distinctive des éléments qui nécessitent des keys
. Regardez les informations que vous stockez dans les widgets respectifs.
Considérez l'application de tâches suivante [1], où vous pouvez modifier l'ordre des éléments dans la liste des tâches en fonction de la priorité et, une fois terminé, vous pouvez les supprimer.

ValueKey
Dans ce cas, nous pouvons nous attendre à ce que le texte du paragraphe sur la mise en œuvre soit permanent et unique. Si c'est le cas, alors c'est probablement un bon candidat pour ValueKey
, où le texte est "valeur".
return TodoItem( key: ValueKey(todo.task), todo: todo, onDismissed: (direction) => _removeTodo(context, todo), );
Objectkey
Vous pouvez également disposer de l'application Carnet d'adresses, qui répertorie les informations sur chaque utilisateur. Dans ce cas, chaque widget enfant stocke une combinaison de données plus complexe. Tous les champs individuels, par exemple, nom ou anniversaire, peuvent coïncider avec une autre entrée, mais la combinaison est unique. Dans ce cas, ObjectKey
est probablement le meilleur ajustement.

Uniquekey
Si vous avez plusieurs widgets dans la collection avec la même valeur ou si vous voulez vraiment vous assurer que chaque widget est différent de tous les autres, vous pouvez utiliser UniqueKey
. J'ai utilisé UniqueKey
dans l'exemple d'application pour changer de couleur, car nous n'avions pas d'autres données constantes qui seraient stockées dans nos widgets, et nous ne savions pas de quelle couleur le widget aurait lors de sa création.
Cependant, une chose que vous ne voulez pas utiliser comme key
est un nombre aléatoire. Chaque fois qu'un widget est créé, un nouveau nombre aléatoire sera généré et vous perdrez la cohérence entre les images. Dans ce scénario, vous ne pouvez pas utiliser du tout de keys
!
PageStorageKeys
PageStorageKeys
sont des keys
spécialisées qui contiennent l'état actuel du défilement afin que l'application puisse l'enregistrer pour une utilisation ultérieure.

Globalkeys
Il existe deux options pour utiliser GlobalKeys
: elles permettent aux widgets de changer de parents n'importe où dans l'application sans perdre leur état et peuvent être utilisées pour accéder aux informations sur un autre widget dans une partie complètement différente de l'arborescence des widgets. Comme exemple du premier scénario, vous pouvez imaginer que vous souhaitez afficher le même widget sur deux écrans différents, mais avec le même état, pour que les données du widget soient enregistrées, vous utiliserez GlobalKey
. Dans le second cas, une situation peut se produire lorsque vous devez vérifier le mot de passe, mais que vous ne souhaitez pas partager les informations d'état avec d'autres widgets de l'arborescence. GlobalKeys
peut également être utile pour les tests, en utilisant la key
pour accéder à un widget spécifique et demander des informations sur son état.

Souvent (mais pas toujours!) Les GlobalKeys
peu comme des variables globales. Souvent, ils peuvent être remplacés par InheritedWidgets
ou quelque chose comme Redux ou le modèle BLoC.
Brève conclusion
En général, utilisez Keys
si vous souhaitez conserver l'état entre les sous-arborescences de widget. Cela se produit le plus souvent lorsque vous modifiez la collection de widgets du même type. Placez la key
en haut de la sous-arborescence du widget que vous souhaitez enregistrer et sélectionnez le type de key
fonction des données stockées dans le widget.
Félicitations, vous êtes maintenant en passe de devenir un mage Flutter! Oh j'ai dit un magicien? Je voulais dire le magicien [2], comme celui qui écrit le code source de l'application ... qui est presque aussi bon. ... presque.
[1] Inspiration pour écrire le code d'application To-do reçu ici
https://github.com/brianegan/flutter_architecture_samples/tree/master/vanilla
[2] L'auteur utilise le mot sorcerer
et y ajoute plus tard une lettre supplémentaire avant le sourcerer