
Der key
ist in fast jedem Widget-Konstruktor zu finden, wird jedoch in der Entwicklung selten verwendet. Keys
behalten den Status bei, wenn Widgets im Widget-Baum verschoben werden. In der Praxis bedeutet dies, dass sie nützlich sein können, um den Bildlaufort oder den Speicherstatus des Benutzers zu speichern, wenn sich die Sammlung ändert.
Dieser Artikel wurde aus dem folgenden Video angepasst. Wenn Sie lieber hören / sehen als lesen möchten, erhalten Sie im Video das gleiche Material.
Meistens ... brauchst du keine keys
. Im Allgemeinen schadet das Hinzufügen nicht, dies ist jedoch auch nicht erforderlich, da sie einfach als neues Schlüsselwort oder als neue Typdeklaration auf beiden Seiten einer neuen Variablen erfolgen (ich spreche von Ihnen, Map<Foo, Bar> aMap = Map<Foo, Bar>()
).
Wenn Sie jedoch feststellen, dass Sie Widgets in der Sammlung hinzufügen, entfernen oder neu anordnen, die einen bestimmten Status enthalten und vom gleichen Typ sind, sollten Sie auf die keys
achten!
Um zu demonstrieren, warum Sie beim Ändern einer Sammlung von Widgets keys
benötigen, habe ich eine äußerst einfache Anwendung mit zwei farbenfrohen Widgets geschrieben, die beim Klicken auf eine Schaltfläche die Position ändern:

In dieser Version der Anwendung habe ich zwei zustandslose Widgets mit zufälliger Farbe ( StatelessWidget
) im Row
und PositionedTiles-Widget mit dem Status ( StatefulWidget
), um die Reihenfolge der StatefulWidget
zu speichern. Wenn ich unten auf die Schaltfläche FloatingActionButton
klicke, ändern die Farb-Widgets ihre Position in der Liste korrekt:
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))); } }
Wenn wir jedoch unseren Farb-Widgets Status hinzufügen (sie zu StatefulWidget
) und die Farbe darin speichern, sieht es so aus, als ob nichts passiert, wenn wir auf die Schaltfläche klicken:

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), )); } }
Zur Erklärung: Der obige Code ist insofern fehlerhaft, als er den Farbaustausch nicht anzeigt, wenn der Benutzer auf die Schaltfläche klickt. Um diesen Fehler zu beheben, müssen Sie den farbigen StatefulWidget
Widgets den key
hinzufügen. Anschließend werden die Widgets wie gewünscht ausgetauscht:

List<Widget> tiles = [ StatefulColorfulTile(key: UniqueKey()),
Dies ist jedoch nur erforderlich, wenn Sie Widgets mit Status in dem Teilbaum haben, den Sie ändern. Wenn der gesamte Teilbaum des Widgets in Ihrer Sammlung keinen Status hat, werden keine Schlüssel benötigt.
So! Alles in allem alles, was Sie wissen müssen, um keys
in Flutter
. Aber wenn Sie etwas tiefer in das Geschehen einsteigen wollen ...
Verstehen, warum keys
manchmal notwendig sind
Du bist immer noch hier, oder? Dann kommen Sie näher und entdecken Sie die wahre Natur von Elementbäumen und Widgets, um ein Flatter-Magier zu werden! Wahahaha! Ha ha! Ha ha! Tut mir leid.
Wie Sie wissen, erstellt Flutter in jedem Widget das entsprechende Element. So wie Flutter einen Widget-Baum erstellt, erstellt er auch einen Elementbaum (ElementTree). ElementTree ist extrem einfach, es enthält nur die Typinformationen jedes Widgets und einen Link zu untergeordneten Elementen. Sie können sich ElementTree als das Grundgerüst Ihrer Flutter-Anwendung vorstellen. Es zeigt die Struktur Ihrer Anwendung, aber alle zusätzlichen Informationen finden Sie unter dem Link zum Quell-Widget.
Das Zeilen-Widget im obigen Beispiel enthält eine Reihe geordneter Slots für jedes seiner untergeordneten Elemente. Wenn wir die Farb-Widgets in Row neu anordnen, geht Flutter um ElementTree herum, um zu überprüfen, ob die Skelettstruktur der Anwendung identisch ist.

Die Validierung beginnt mit einem RowElement und geht dann zu den untergeordneten Elementen über. ElementTree überprüft, ob das neue Widget denselben Typ und key
wie das alte hat. In diesem Fall aktualisiert das Element seinen Link zum neuen Widget. In der zustandslosen Version des Codes haben Widgets keinen key
, daher überprüft Flutter einfach nur den Typ. (Wenn zu viele Informationen gleichzeitig vorhanden sind, sehen Sie sich das animierte Diagramm oben an.)
Unter ElementTree für Status-Widgets sieht es etwas anders aus. Nach wie vor gibt es Widgets und Elemente, aber es gibt auch einige Statusobjekte für Widgets, in denen Farbinformationen und nicht in den Widgets selbst gespeichert sind.

Bei farbigen StatefulWidget
Widgets ohne key
geht Flutter beim Ändern der Reihenfolge von zwei Widgets um ElementTree herum, überprüft den Typ des RowWidget und aktualisiert den Link. Anschließend überprüft das Farb-Widget-Element, ob das entsprechende Widget vom gleichen Typ ist, und aktualisiert den Link. Das gleiche passiert mit dem zweiten Widget. Da Flutter ElementTree und den entsprechenden Status verwendet, um zu bestimmen, was tatsächlich auf Ihrem Gerät angezeigt werden soll, scheinen aus unserer Sicht die Widgets nicht ausgetauscht worden zu sein!

In der festen Version des Codes in farbigen Widgets mit Status im Konstruktor habe ich die key
definiert. Wenn wir nun die Widgets in Row
ändern, stimmen sie nach Typ wie zuvor überein, aber die key
für das Farb-Widget und für das entsprechende Element in ElementTree sind unterschiedlich. Dies führt dazu, dass Flutter diese Elemente der Farb-Widgets deaktiviert und die Verknüpfungen zu ihnen in ElementTree entfernt, beginnend mit dem ersten, der nicht mit dem key
übereinstimmt.

Anschließend sucht Flutter im ElementTree mit dem entsprechenden key
nach den Widgets im Zeilenelement. Wenn es übereinstimmt, wird ein Link zum Widget-Element hinzugefügt. Flattern tut für jedes Kind ohne Link. Jetzt zeigt Flutter an, was wir erwarten. Die Farb-Widgets ändern ihre Position, wenn ich auf die Schaltfläche klicke.
Daher sind keys
nützlich, wenn Sie die Reihenfolge oder Anzahl der Widgets mit dem Status in der Sammlung ändern. In diesem Beispiel habe ich die Farbe gespeichert. Oft ist der Zustand jedoch nicht so offensichtlich. Eine Animation abspielen, Benutzereingaben anzeigen und durch einen Ort scrollen - alles hat einen Status.
Wann sollte ich keys
?
Kurze Antwort: Wenn Sie der Anwendung keys
hinzufügen müssen, sollten Sie diese oben im Widget-Teilbaum mit dem Status hinzufügen, den Sie speichern möchten.
Ein häufiger Fehler, den ich gesehen habe, ist, dass die Leute denken, dass sie den key
nur für das erste Widget mit Status definieren müssen, aber es gibt Nuancen. Glaubst du mir nicht? Um zu zeigen, in welchen Problemen wir uns befinden könnten, habe ich meine Farb-Widgets in Padding
Widgets eingeschlossen und die Tasten für die Farb-Widgets belassen.
void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> {
Jetzt erhalten Widgets auf Knopfdruck völlig zufällige Farben!

So sehen der Widget-Baum und ElementTree mit den hinzugefügten Padding
Widgets aus:

Wenn wir die Positionen von untergeordneten Widgets ändern, sieht der Übereinstimmungsalgorithmus zwischen Elementen und Widgets eine Ebene im Elementbaum aus. Im Diagramm sind die Kinder der Kinder abgedunkelt, so dass nichts von der ersten Ebene ablenkt. Auf dieser Ebene stimmt alles richtig überein.

Auf der zweiten Ebene stellt Flutter fest, dass der key
Farbelements nicht mit dem key
Widgets übereinstimmt. Daher wird dieses Element deaktiviert, verworfen und alle Links zu ihm entfernt. keys
in diesem Beispiel verwendeten keys
sind LocalKeys
. Dies bedeutet, dass Flutter beim Abgleichen eines Widgets mit Elementen nur auf einer bestimmten Ebene des Baums nach keys
sucht.
Da er das Farb-Widget-Element auf dieser Ebene mit dem entsprechenden key
nicht finden kann, erstellt er ein neues und initialisiert einen neuen Status, wodurch das Widget in diesem Fall orange wird!

Wenn wir keys
für Padding
Widgets definieren:
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 bemerkt das Problem und aktualisiert die Links korrekt, wie in unserem vorherigen Beispiel. Die Ordnung im Universum wird wiederhergestellt.

Welche Art von Key
soll ich verwenden?
Flutter-APIs gaben uns die Wahl zwischen mehreren Key
. Welche Art von key
Sie verwenden sollten, hängt davon ab, was die Unterscheidungsmerkmale von Elementen sind, die keys
benötigen. Sehen Sie sich die Informationen an, die Sie in den jeweiligen Widgets speichern.
Stellen Sie sich die folgende Aufgabenanwendung [1] vor, in der Sie die Reihenfolge der Elemente in der Aufgabenliste basierend auf der Priorität ändern und anschließend löschen können.

ValueKey
In diesem Fall können wir erwarten, dass der Text des Absatzes über die Implementierung dauerhaft und eindeutig ist. Wenn ja, dann ist dies wahrscheinlich ein guter Kandidat für ValueKey
, wo der Text "Wert" ist.
return TodoItem( key: ValueKey(todo.task), todo: todo, onDismissed: (direction) => _removeTodo(context, todo), );
Objektschlüssel
Alternativ können Sie die Adressbuchanwendung verwenden, in der Informationen zu jedem Benutzer aufgelistet sind. In diesem Fall speichert jedes untergeordnete Widget eine komplexere Kombination von Daten. Jedes der einzelnen Felder, z. B. Name oder Geburtstag, kann mit einem anderen Eintrag übereinstimmen, die Kombination ist jedoch eindeutig. In diesem Fall ist ObjectKey
höchstwahrscheinlich die beste ObjectKey
.

Uniquekey
Wenn Sie mehrere Widgets in der Sammlung mit demselben Wert haben oder wirklich sicherstellen möchten, dass sich jedes Widget von allen anderen unterscheidet, können Sie UniqueKey
. Ich habe UniqueKey
in der Beispielanwendung zum Wechseln der Farben verwendet, da wir keine anderen konstanten Daten hatten, die in unseren Widgets gespeichert würden, und wir nicht wussten, welche Farbe das Widget beim Erstellen haben würde.
Eine Sache, die Sie jedoch nicht als key
möchten, ist eine Zufallszahl. Jedes Mal, wenn ein Widget erstellt wird, wird eine neue Zufallszahl generiert und Sie verlieren die Konsistenz zwischen den Frames. In diesem Szenario dürfen Sie überhaupt keine keys
!
PageStorageKeys
PageStorageKeys
sind spezielle keys
, die den aktuellen Status des PageStorageKeys
enthalten, damit die Anwendung ihn zur späteren Verwendung speichern kann.

Globalkeys
Es gibt zwei Optionen für die Verwendung von GlobalKeys
: Sie ermöglichen Widgets, die GlobalKeys
an einer beliebigen Stelle in der Anwendung zu ändern, ohne den Status zu verlieren, und können verwendet werden, um auf Informationen zu einem anderen Widget in einem völlig anderen Teil des Widget-Baums zuzugreifen. Als Beispiel für das erste Szenario können Sie sich vorstellen, dass Sie dasselbe Widget auf zwei verschiedenen Bildschirmen GlobalKey
möchten, aber mit demselben Status, damit die GlobalKey
gespeichert werden, verwenden Sie GlobalKey
. Im zweiten Fall kann es vorkommen, dass Sie das Kennwort überprüfen müssen, aber keine Statusinformationen für andere Widgets in der Baumstruktur freigeben möchten. GlobalKeys
kann auch zum Testen nützlich sein, indem der key
um auf ein bestimmtes Widget zuzugreifen und Informationen über dessen Status anzufordern.

Oft (aber nicht immer!) GlobalKeys
bisschen wie globale Variablen. Oft können sie durch InheritedWidgets
oder etwas wie Redux oder die BLoC-Vorlage ersetzt werden.
Kurzer Abschluss
Verwenden Sie im Allgemeinen Keys
wenn Sie den Status zwischen Widget-Teilbäumen beibehalten möchten. Dies geschieht am häufigsten, wenn Sie die Sammlung von Widgets desselben Typs ändern. Platzieren Sie den key
oben im Widget-Teilbaum, den Sie speichern möchten, und wählen Sie den key
basierend auf den im Widget gespeicherten Daten aus.
Herzlichen Glückwunsch, Sie sind jetzt auf dem Weg, ein Flatter-Magier zu werden! Oh, ich sagte ein Zauberer? Ich meinte den Zauberer [2], wie denjenigen, der den Anwendungsquellcode schreibt ... was fast genauso gut ist. ... fast.
[1] Inspiration zum Schreiben des hier erhaltenen To-Do-Anwendungscodes
https://github.com/brianegan/flutter_architecture_samples/tree/master/vanilla
[2] Der Autor verwendet das Wort sorcerer
und fügt später vor dem sourcerer
einen zusätzlichen Buchstaben sourcerer