
El par谩metro key
se puede encontrar en casi todos los constructores de widgets, pero este par谩metro rara vez se usa en el desarrollo. Keys
retienen el estado al mover widgets en el 谩rbol de widgets. En la pr谩ctica, esto significa que pueden ser 煤tiles para guardar la ubicaci贸n de desplazamiento del usuario o guardar el estado cuando cambia la colecci贸n.
Este art铆culo est谩 adaptado del siguiente video . Si prefiere escuchar / mirar en lugar de leer, el video le proporcionar谩 el mismo material.
La mayor铆a de las veces ... no necesitas keys
. En general, no es perjudicial agregarlos, pero esto tampoco es necesario, ya que simplemente tienen lugar como una nueva palabra clave o declaraci贸n de tipo en ambos lados de una nueva variable (estoy hablando de ti, Map<Foo, Bar> aMap = Map<Foo, Bar>()
).
Pero si descubre que est谩 agregando, eliminando o reorganizando widgets en la colecci贸n que contienen alg煤n estado y son del mismo tipo, 隆entonces debe prestar atenci贸n a las keys
!
Para demostrar por qu茅 necesita keys
al cambiar una colecci贸n de widgets, escrib铆 una aplicaci贸n extremadamente simple con dos widgets coloridos que cambian de lugar cuando se hace clic en un bot贸n:

En esta versi贸n de la aplicaci贸n, tengo dos widgets sin estado de color aleatorio ( StatelessWidget
) en el widget Row
y PositionedTiles con estado ( StatefulWidget
) para almacenar el orden de los widgets de color. Cuando hago clic en el bot贸n FloatingActionButton
en la parte inferior, los widgets de color cambian correctamente su lugar en la lista:
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))); } }
Pero si agregamos estado a nuestros widgets de color (los hacemos StatefulWidget
) y almacenamos el color en ellos, entonces cuando hacemos clic en el bot贸n, parece que no sucede nada:

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), )); } }
Como explicaci贸n: el c贸digo anterior tiene errores porque no muestra el intercambio de color cuando el usuario hace clic en el bot贸n. Para corregir este error, debe agregar el par谩metro key
a los widgets StatefulWidget
coloreados, y luego los widgets se intercambiar谩n como queramos:

List<Widget> tiles = [ StatefulColorfulTile(key: UniqueKey()),
Pero esto solo es necesario si tiene widgets con estado en el sub谩rbol que est谩 cambiando. Si todo el sub谩rbol del widget en su colecci贸n no tiene estado, no se necesitan claves.
Ah铆 tienes! Con todo, todo lo que necesita saber para usar las keys
en Flutter
. Pero si quieres profundizar un poco m谩s en lo que est谩 sucediendo ...
Comprender por qu茅 las keys
veces keys
necesarias
Todav铆a est谩s aqu铆, 驴verdad? 隆Pues ac茅rcate para descubrir la verdadera naturaleza de los 谩rboles y widgets de elementos para convertirte en un mago revoloteador! Wahahaha! Jaja Jaja Lo siento.
Como sabes, dentro de cada widget Flutter construye el elemento correspondiente. As铆 como Flutter construye un 谩rbol de widgets, tambi茅n crea un 谩rbol de elementos (ElementTree). ElementTree es extremadamente simple, contiene solo la informaci贸n de tipo de cada widget y un enlace a elementos secundarios. Puede pensar en ElementTree como el esqueleto de su aplicaci贸n Flutter. Muestra la estructura de su aplicaci贸n, pero toda la informaci贸n adicional se puede encontrar en el enlace al widget de origen.
El widget de fila del ejemplo anterior contiene un conjunto de ranuras ordenadas para cada uno de sus elementos secundarios. Cuando volvemos a ordenar los widgets de color en Row, Flutter recorre ElementTree para comprobar si la estructura esquel茅tica de la aplicaci贸n es la misma.

La validaci贸n comienza con un RowElement y luego pasa a los elementos secundarios. ElementTree verifica que el nuevo widget tenga el mismo tipo y key
que el anterior, y si es as铆, el elemento actualiza su enlace al nuevo widget. En la versi贸n sin estado del c贸digo, los widgets no tienen key
, por lo que Flutter simplemente verifica solo el tipo. (Si hay demasiada informaci贸n a la vez, consulte la tabla animada anterior).
Debajo de ElementTree para widgets de estado se ve un poco diferente. Hay widgets y elementos, como antes, pero tambi茅n hay un par de objetos de estado para widgets, y la informaci贸n de color se almacena en ellos, y no en los widgets.

En el caso de los widgets de StatefulWidget
coloreados sin key
, cuando cambio el orden de dos widgets, Flutter recorre ElementTree, verifica el tipo de RowWidget y actualiza el enlace. Luego, el elemento de widget de color verifica que el widget correspondiente sea del mismo tipo y actualiza el enlace. Lo mismo sucede con el segundo widget. Dado que Flutter usa ElementTree y su estado correspondiente para determinar qu茅 mostrar realmente en su dispositivo, desde nuestro punto de vista, 隆parece que los widgets no se han intercambiado!

En la versi贸n fija del c贸digo en widgets de colores con estado en el constructor, defin铆 la propiedad key
. Ahora, si cambiamos los widgets en Row
, por tipo coincidir谩n como antes, pero los valores key
del widget de color y el elemento correspondiente en ElementTree ser谩n diferentes. Esto hace que Flutter desactive estos elementos de los widgets de color y elimine los enlaces a ellos en ElementTree, comenzando por el primero, que no coincide con la key
.

Luego, Flutter busca los widgets en el elemento Row
en ElementTree con la key
correspondiente. Si coincide, agrega un enlace al elemento del widget. Flutter lo hace para cada ni帽o sin un enlace. Ahora Flutter mostrar谩 lo que esperamos, los widgets de color cambiar谩n de lugar cuando haga clic en el bot贸n.
Por lo tanto, las keys
son 煤tiles si cambia el orden o el n煤mero de widgets con estado en la colecci贸n. En este ejemplo, guard茅 el color. Sin embargo, a menudo la condici贸n no es tan obvia. Reproducir una animaci贸n, mostrar la entrada del usuario y desplazarse por una ubicaci贸n: todo tiene un estado.
驴Cu谩ndo debo usar las keys
?
Respuesta corta: si necesita agregar keys
a la aplicaci贸n, debe agregarlas en la parte superior del sub谩rbol de widgets con el estado que desea guardar.
Un error com煤n que vi es que las personas piensan que necesitan definir la key
solo para el primer widget con estado, pero hay matices. No me creas Para mostrar en qu茅 problemas podr铆amos estar, envolv铆 mis widgets de color en widgets de Padding
, mientras dejaba las teclas para los widgets de color.
void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> {
隆Ahora, con solo tocar un bot贸n, los widgets obtienen colores completamente al azar!

As铆 es como se ven el 谩rbol de widgets y ElementTree con los widgets Padding
agregados:

Cuando cambiamos las posiciones de los widgets secundarios, el algoritmo de coincidencia entre elementos y widgets se ve un nivel en el 谩rbol de elementos. En el diagrama, los hijos de los ni帽os se oscurecen para que nada distraiga del primer nivel. En este nivel, todo coincide correctamente.

En el segundo nivel, Flutter se da cuenta de que la key
elemento de color no coincide con la key
widget, por lo que desactiva este elemento, lo descarta y elimina todos los enlaces. keys
que usamos en este ejemplo son LocalKeys
. Esto significa que al hacer coincidir un widget con elementos, Flutter busca keys
solo en un cierto nivel del 谩rbol.
Como no puede encontrar el elemento de widget de color en este nivel con la key
correspondiente, crea uno nuevo e inicializa un nuevo estado, 隆haciendo que el widget sea naranja en este caso!

Si definimos keys
para 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 nota el problema y actualiza los enlaces correctamente, como sucedi贸 en nuestro ejemplo anterior. Se restaura el orden en el universo.

驴Qu茅 tipo de Key
debo usar?
Las API de Flutter nos dieron la opci贸n de varias clases Key
. El tipo de key
que debe usar depende de cu谩l es la caracter铆stica distintiva de los elementos que necesitan keys
. Mire la informaci贸n que almacena en los widgets respectivos.
Considere la siguiente aplicaci贸n Tareas pendientes [1], donde puede cambiar el orden de los elementos en la lista de tareas seg煤n la prioridad y, cuando haya terminado, puede eliminarlos.

ValueKey
En este caso, podemos esperar que el texto del p谩rrafo sobre la implementaci贸n sea permanente y 煤nico. Si es as铆, entonces este es probablemente un buen candidato para ValueKey
, donde el texto es "valor".
return TodoItem( key: ValueKey(todo.task), todo: todo, onDismissed: (direction) => _removeTodo(context, todo), );
Objectkey
Alternativamente, puede tener la aplicaci贸n Libreta de direcciones, que enumera informaci贸n sobre cada usuario. En este caso, cada widget secundario almacena una combinaci贸n m谩s compleja de datos. Cualquiera de los campos individuales, por ejemplo, nombre o fecha de nacimiento, puede coincidir con otra entrada, pero la combinaci贸n es 煤nica. En este caso, ObjectKey
es probablemente el mejor ajuste.

Uniquekey
Si tiene varios widgets en la colecci贸n con el mismo valor o si realmente quiere asegurarse de que cada widget sea diferente de todos los dem谩s, puede usar UniqueKey
. UniqueKey
en la aplicaci贸n de ejemplo para cambiar los colores, porque no ten铆amos otros datos constantes que se almacenar铆an en nuestros widgets, y no sab铆amos qu茅 color tendr铆a el widget al crearlo.
Sin embargo, una cosa que no desea utilizar como key
es un n煤mero aleatorio. Cada vez que se crea un widget, se generar谩 un nuevo n煤mero aleatorio y perder谩 la coherencia entre los marcos. En este escenario, 隆no puede usar keys
en absoluto!
PageStorageKeys
PageStorageKeys
son keys
especializadas que contienen el estado actual del desplazamiento para que la aplicaci贸n pueda guardarlo para su uso posterior.

Globalkeys
Hay dos opciones para usar GlobalKeys
: permiten que los widgets cambien a los padres en cualquier lugar de la aplicaci贸n sin perder el estado y se pueden usar para acceder a informaci贸n sobre otro widget en una parte completamente diferente del 谩rbol de widgets. Como ejemplo del primer escenario, puede imaginar que desea mostrar el mismo widget en dos pantallas diferentes, pero con el mismo estado, para que se guarden los datos del widget, usar谩 GlobalKey
. En el segundo caso, puede surgir una situaci贸n en la que necesite verificar la contrase帽a, pero no desea compartir informaci贸n de estado con otros widgets en el 谩rbol. GlobalKeys
tambi茅n puede ser 煤til para las pruebas, utilizando la key
para acceder a un widget espec铆fico y solicitar informaci贸n sobre su estado.

A menudo (隆pero no siempre!) GlobalKeys
poco como las variables globales. A menudo se pueden reemplazar con InheritedWidgets
o algo as铆 como Redux, o la plantilla BLoC.
Breve conclusi贸n
En general, use Keys
si desea mantener el estado entre los sub谩rboles de widgets. Esto ocurre con mayor frecuencia cuando cambia la colecci贸n de widgets del mismo tipo. Coloque la key
en la parte superior del sub谩rbol de widgets que desea guardar y seleccione el tipo de key
funci贸n de los datos almacenados en el widget.
隆Felicitaciones, ahora est谩s en camino de convertirte en un mago revoloteador! Oh dije un mago? Me refer铆a al mago [2], como el que escribe el c贸digo fuente de la aplicaci贸n ... que es casi tan bueno. ... casi.
[1] Inspiraci贸n para escribir el c贸digo de la aplicaci贸n Tareas recibidas aqu铆
https://github.com/brianegan/flutter_architecture_samples/tree/master/vanilla
[2] El autor usa la palabra sorcerer
y luego le agrega una carta adicional antes del sourcerer