Keys
En Flutter, le Key est un identifiant unique que vous pouvez attribuer à un widget. Il joue un rôle crucial dans la gestion de l'état des widgets lors des reconstructions de l'arbre des widgets.
Pourquoi utiliser des clés ?
Lorsqu'un widget est reconstruit (par exemple, en raison d'un changement d'état), Flutter doit déterminer si les nouveaux widgets correspondent aux anciens. Par défaut, il se base sur le type et l'ordre des widgets. Cependant, dans certaines situations, cela peut poser problème :
- Listes dynamiques: Si l'ordre des éléments d'une liste change, Flutter peut reconstruire inutilement des widgets, ce qui peut nuire aux performances.
- Widgets avec état: Si un widget avec état est déplacé dans l'arbre, Flutter peut perdre son état.
C'est là qu'interviennent les clés. Elles permettent à Flutter d'identifier de manière unique chaque widget, même si son type ou sa position change. Ainsi, Flutter peut conserver l'état des widgets et optimiser les reconstructions.
Types de clés
Flutter propose différents types de clés, chacun ayant un rôle spécifique :
- ValueKey: Utilisée pour identifier un widget en fonction d'une valeur (par exemple, un identifiant unique).
- ObjectKey: Similaire à
ValueKey, mais utilisée pour identifier un widget en fonction d'un objet. - UniqueKey: Utilisée pour attribuer un identifiant unique à chaque widget.
- GlobalKey: Utilisée pour accéder à un widget depuis n'importe où dans l'application.
Quand utiliser les clés ?
Il est recommandé d'utiliser des clés dans les situations suivantes :
- Listes dynamiques: Attribuez une
ValueKeyà chaque élément de la liste en utilisant un identifiant unique. - Widgets avec état: Attribuez une clé au widget avec état pour conserver son état lors des reconstructions.
- Widgets identiques: Si vous avez plusieurs widgets identiques au même niveau de l'arbre, utilisez des clés différentes pour les distinguer.
1. Drag & Drop pour réordonner une liste
Imaginez une application où l'utilisateur peut réorganiser une liste de tâches par drag & drop. Avec ValueKey, chaque tâche reste identifiée même si sa position change. Sans clé, les éléments se mélangent !
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class Tache {
final int id;
final String titre;
const Tache({required this.id, required this.titre});
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(home: ListeTaches());
}
}
class ListeTaches extends StatefulWidget {
const ListeTaches({super.key});
State<ListeTaches> createState() => _ListeTachesState();
}
class _ListeTachesState extends State<ListeTaches> {
final List<Tache> taches = [
const Tache(id: 1, titre: 'Faire les courses'),
const Tache(id: 2, titre: 'Nettoyer la maison'),
const Tache(id: 3, titre: 'Préparer le dîner'),
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Drag & Drop avec clés')),
body: ReorderableListView(
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (newIndex > oldIndex) newIndex -= 1;
final tache = taches.removeAt(oldIndex);
taches.insert(newIndex, tache);
});
},
children: taches
.map(
(tache) => ListTile(
key: ValueKey(index), //
leading: const Icon(Icons.drag_handle),
title: Text(tache.titre),
),
)
.toList(),
),
);
}
}
2. Conserver l'état d'un widget lors d'un changement de parent
Supposons que vous ayez un widget personnalisé avec un état interne (par exemple, un compteur) et que vous souhaitiez le déplacer dans l'arbre des widgets. Sans clé, l'état du widget serait perdu lors du déplacement.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('UniqueKey')),
body: Center(
child: CompteurWidget(key: UniqueKey()),
),
),
);
}
}
class CompteurWidget extends StatefulWidget {
const CompteurWidget({super.key});
State<CompteurWidget> createState() => _CompteurWidgetState();
}
class _CompteurWidgetState extends State<CompteurWidget> {
int compteur = 0;
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Compteur: $compteur'),
ElevatedButton(
onPressed: () {
setState(() {
compteur++;
});
},
child: const Text('Incrémenter'),
),
],
);
}
}
En attribuant une UniqueKey au widget CompteurWidget, vous vous assurez que son état est conservé même s'il est déplacé dans l'arbre des widgets.
3. Accéder à un widget spécifique
Dans certains cas, vous pourriez avoir besoin d'accéder à un widget spécifique depuis un autre endroit de votre application. C'est là que les GlobalKey peuvent être utiles.
import 'package:flutter/material.dart';
final GlobalKey<_MonWidgetState> _monWidgetKey = GlobalKey<_MonWidgetState>();
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('GlobalKey')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MonWidget(key: _monWidgetKey),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
_monWidgetKey.currentState?.changerTexte('Au revoir');
},
child: const Text('Changer le texte'),
),
],
),
),
);
}
}
class MonWidget extends StatefulWidget {
const MonWidget({super.key});
State<MonWidget> createState() => _MonWidgetState();
}
class _MonWidgetState extends State<MonWidget> {
String texte = 'Bonjour';
void changerTexte(String nouveauTexte) {
setState(() {
texte = nouveauTexte;
});
}
Widget build(BuildContext context) {
return Text(texte, style: const TextStyle(fontSize: 18));
}
}
Dans cet exemple, _monWidgetKey est une GlobalKey qui permet d'accéder à l'instance de _MonWidgetState. Vous pouvez ensuite utiliser cette instance pour appeler des méthodes ou accéder à des propriétés du widget.