フラッタヌ。 キヌ 圌らは䜕のために


keyパラメヌタヌは、ほずんどすべおのりィゞェットコンストラクタヌで芋぀けるこずができたすが、このパラメヌタヌは開発ではほずんど䜿甚されたせん。 Keysは、りィゞェットツリヌ内のりィゞェットを移動するずきに状態を保持したす。 実際には、これは、コレクションの倉曎時にナヌザヌのスクロヌル䜍眮を保存したり、状態を保存したりするのに圹立぀可胜性があるこずを意味したす。


この蚘事は、次のビデオからの抜粋です。 読むよりも聞く/芋るこずを奜む堎合、ビデオは同じ玠材を提䟛したす。


keysに関する秘密情報


ほずんどの堎合... keysは必芁ありたせん。 䞀般に、それらを远加しおも害はありたせんが、新しい倉数の䞡偎で新しいキヌワヌドたたは型宣蚀ずしお行われるため、これも必芁ありたせん私はあなたに぀いお話しおいる、 Map<Foo, Bar> aMap = Map<Foo, Bar>() 。


ただし、䜕らかの状態を含み、同じタむプのりィゞェットをコレクション内で远加、削陀、たたは再配眮しおいる堎合は、 keys泚意する必芁がありkeys 

りィゞェットのコレクションを倉曎するずきにkeysが必芁な理由を瀺すために、ボタンがクリックされたずきに堎所を倉曎する2぀のカラフルなりィゞェットを持぀非垞にシンプルなアプリケヌションを䜜成したした。



このバヌゞョンのアプリケヌションでは、色りィゞェットの順序を栌玍するために、 RowずStateedTilesりィゞェットに状態 StatefulWidget のランダムな色の2぀のステヌトレスりィゞェット StatelessWidget がありたす。 䞋のFloatingActionButtonボタンをクリックするず、色りィゞェットがリスト内の䜍眮を正しく倉曎したす。


 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))); } } 

しかし、カラヌりィゞェットに状態を远加しお StatefulWidget 、色を保存するず、ボタンをクリックするず、䜕も起きおいないように芋えたす。



 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), )); } } 

説明ずしお䞊蚘のコヌドは、ナヌザヌがボタンをクリックしおも色の亀換が衚瀺されないずいう点でバグがありたす。 この゚ラヌを修正するには、色付きのStatefulWidgetりィゞェットにkeyパラメヌタヌを远加する必芁がありたす。そうするず、りィゞェットは必芁に応じお亀換されたす。



 List<Widget> tiles = [ StatefulColorfulTile(key: UniqueKey()), // Keys added here StatefulColorfulTile(key: UniqueKey()), ]; ... class StatefulColorfulTile extends StatefulWidget { StatefulColorfulTile({Key key}) : super(key: key); // NEW CONSTRUCTOR @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), )); } } 

ただし、これは、倉曎するサブツリヌに状態を持぀りィゞェットがある堎合にのみ必芁です。 コレクション内のりィゞェットのサブツリヌ党䜓に状態がない堎合、キヌは必芁ありたせん。
行くぞ すべおのすべお、 Flutter keysを䜿甚するために知っおおく必芁があるすべお。 しかし、あなたが起こっおいるこずに少し深く行きたいなら...




keys必芁な堎合keysある理由を理解する


あなたはただここにいたすよね それでは、芁玠ツリヌずりィゞェットの真の性質を調べお、フラッタヌメむゞになろう わははは ハハ ハハ ごめんなさい


ご存じのずおり、各りィゞェット内でFlutterは察応する芁玠を䜜成したす。 Flutterがりィゞェットツリヌを䜜成するように、芁玠ツリヌElementTreeも䜜成したす。 ElementTreeは非垞にシンプルで、各りィゞェットのタむプ情報ず子芁玠ぞのリンクのみが含たれおいたす。 ElementTreeをFlutterアプリケヌションのスケルトンず考えるこずができたす。 アプリケヌションの構造を瀺しおいたすが、远加情報はすべお゜ヌスりィゞェットぞのリンクにありたす。


䞊蚘の䟋の行りィゞェットには、その子ごずに順序付けられたスロットのセットが含たれおいたす。 Rowのカラヌりィゞェットの順序を倉曎するず、FlutterはElementTreeを歩き回り、アプリケヌションのスケルトン構造が同じかどうかを確認したす。



怜蚌はRowElementで始たり、次に子芁玠に進みたす。 ElementTreeは、新しいりィゞェットが叀いものず同じタむプずkeyを持っおいるこずをチェックし、もしそうなら、芁玠は新しいりィゞェットぞのリンクを曎新したす。 コヌドのステヌトレスバヌゞョンでは、りィゞェットにkeyがないため、Flutterはタむプのみをチェックしたす。 䞀床に情報が倚すぎる堎合は、䞊蚘のアニメヌションチャヌトを参照しおください。


状態りィゞェットのElementTreeの䞋は少し異なりたす。 前ず同じようにりィゞェットず芁玠がありたすが、りィゞェットの状態オブゞェクトもいく぀かあり、色情報はりィゞェット自䜓ではなく、それらに保存されたす。



keyない色付きのStatefulWidgetりィゞェットの堎合、2぀のりィゞェットの順序を倉曎するず、FlutterはElementTreeを巡回し、RowWidgetのタむプをチェックしお、リンクを曎新したす。 次に、カラヌりィゞェット芁玠は、察応するりィゞェットが同じタむプであるこずを確認し、リンクを曎新したす。 2番目のりィゞェットでも同じこずが起こりたす。 FlutterはElementTreeずそれに察応する状態を䜿甚しお、実際にデバむスに衚瀺するものを決定するため、私たちの芳点からは、りィゞェットは亀換されおいないようです



コンストラクタヌに状態を持぀色付きりィゞェットのコヌドの修正バヌゞョンでは、 keyプロパティを定矩したした。 ここで、 Rowのりィゞェットを倉曎するず、タむプごずに以前ず同じように䞀臎したすが、カラヌりィゞェットずElementTreeの察応する芁玠のkey倀は異なりたす。 これにより、Flutterはカラヌりィゞェットのこれらの芁玠を非アクティブ化し、ElementTreeでそれらの芁玠ぞのリンクを削陀したす。最初のkeyはkey䞀臎したせん。



その埌、Flutterは、察応するkeyを䜿甚しおElementTreeのRow芁玠でりィゞェットを怜玢しkey 。 䞀臎する堎合、りィゞェット芁玠にリンクを远加したす。 Flutterは、リンクのない各子に察しお行いたす。 これでFlutterに期埅どおりの結果が衚瀺され、ボタンをクリックするずカラヌりィゞェットの堎所が倉わりたす。


したがっお、 keysは、コレクション内の状態でりィゞェットの順序たたは数を倉曎する堎合に圹立ちたす。 この䟋では、色を保存したした。 ただし、倚くの堎合、状態はそれほど明癜ではありたせん。 アニメヌションの再生、ナヌザヌ入力の衚瀺、堎所のスクロヌルなど、すべおの状態がありたす。




い぀keysを䜿甚する必芁がありkeysか


簡単な答え keysをアプリケヌションに远加する必芁がある堎合は、保存したい状態のりィゞェットサブツリヌの䞊郚にkeysを远加する必芁がありたす。


私が芋たよくある間違いは、状態を持぀最初のりィゞェットに察しおのみkeyを定矩する必芁があるず人々が考えるこずですが、ニュアンスがありたす。 信じられない どのような問題があるのか​​を瀺すために、カラヌりィゞェットのキヌを残したたた、カラヌりィゞェットをPaddingりィゞェットでラップしたした。


 void main() => runApp(new MaterialApp(home: PositionedTiles())); class PositionedTiles extends StatefulWidget { @override State<StatefulWidget> createState() => PositionedTilesState(); } class PositionedTilesState extends State<PositionedTiles> { // Stateful tiles now wrapped in padding (a stateless widget) to increase height // of widget tree and show why keys are needed at the Padding level. List<Widget> tiles = [ Padding( padding: const EdgeInsets.all(8.0), child: StatefulColorfulTile(key: UniqueKey()), ), Padding( padding: const EdgeInsets.all(8.0), child: StatefulColorfulTile(key: UniqueKey()), ), ]; @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 StatefulColorfulTile extends StatefulWidget { StatefulColorfulTile({Key key}) : super(key: key); @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), )); } } 

今、ボタンに觊れるだけで、りィゞェットは完党にランダムな色になりたす



これは、远加されたPaddingりィゞェットを䜿甚したりィゞェットツリヌずElementTreeの倖芳です。



子りィゞェットの䜍眮を倉曎するず、芁玠ずりィゞェット間のマッチングアルゎリズムは、芁玠のツリヌの1぀のレベルに芋えたす。 図では、子の子が暗くされおいるため、最初のレベルから䜕も邪魔されたせん。 このレベルでは、すべおが正しく䞀臎したす。



2番目のレベルでは、Flutter key color芁玠のkeyりィゞェットのkey䞀臎しないこずに気づくため、この芁玠を非アクティブ化し、砎棄しお、すべおのリンクを削陀したす。 この䟋で䜿甚するkeysはLocalKeysです。 ぀たり、りィゞェットを芁玠ず䞀臎させるず、Flutterはツリヌの特定のレベルでのみkeys怜玢しkeys 。


圌はこのレベルで察応するkey持぀カラヌりィゞェット芁玠を芋぀けるこずができないため、新しいりィゞェットを䜜成しお新しい状態を初期化し、この堎合りィゞェットをオレンゞ色にしたす



Paddingりィゞェットのkeysを定矩する堎合


 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( // Place the keys at the *top* of the tree of the items in the collection. key: UniqueKey(), padding: const EdgeInsets.all(8.0), child: StatefulColorfulTile(), ), Padding( key: UniqueKey(), padding: const EdgeInsets.all(8.0), child: StatefulColorfulTile(), ), ]; @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 StatefulColorfulTile extends StatefulWidget { StatefulColorfulTile({Key key}) : super(key: key); @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), )); } } 

Flutterは問題を認識し、前の䟋のようにリンクを正しく曎新したす。 ナニバヌスの順序が埩元されたす。





どのタむプのKeyを䜿甚する必芁がありたすか


Flutter APIにより、いく぀かのKeyクラスを遞択できたした。 䜿甚すべきkeyのタむプは、 keyを必芁ずする芁玠の特城的な機胜によっお異なりkeys 。 それぞれのりィゞェットに保存した情報を芋おください。


次のTo-doアプリケヌション[1]を怜蚎しおください。ここでは、優先床に基づいおタスクリスト内のアむテムの順序を倉曎でき、完了したら削陀できたす。



ValueKey
この堎合、実行するアむテムのテキストは氞続的で䞀意であるこずが予想されたす。 もしそうなら、これはおそらくテキストが「倀」であるValueKey良い候補です。


 return TodoItem( key: ValueKey(todo.task), todo: todo, onDismissed: (direction) => _removeTodo(context, todo), ); 

オブゞェクトキヌ
たたは、各ナヌザヌに関する情報を䞀芧衚瀺するアドレス垳アプリケヌションを䜿甚するこずもできたす。 この堎合、各子りィゞェットには、より耇雑なデヌタの組み合わせが栌玍されたす。 名前や誕生日などの個々のフィヌルドは別の゚ントリず䞀臎する堎合がありたすが、組み合わせは䞀意です。 この堎合、 ObjectKeyが最適です。



ナニヌクキヌ
コレクションに同じ倀を持぀耇数のりィゞェットがある堎合、たたは各りィゞェットが他のすべおのりィゞェットず異なるこずを本圓に確認したい堎合は、 UniqueKeyを䜿甚できたす。 サンプルアプリケヌションで色を切り替えるためにUniqueKeyを䜿甚したした。 UniqueKeyは、りィゞェットに栌玍される他の定数デヌタが​​なく、䜜成時にりィゞェットの色がわからないためです。


ただし、 keyずしお䜿甚したくないのは乱数です。 りィゞェットが䜜成されるたびに、新しい乱数が生成され、フレヌム間の䞀貫性が倱われたす。 このシナリオでは、 keysをたったく䜿甚できたせん


PageStorageKeys
PageStorageKeysは、スクロヌルの珟圚の状態を含む特殊なkeysあるため、アプリケヌションは埌で䜿甚するために保存できたす。



グロヌバルキヌ
GlobalKeysを䜿甚するための2぀のオプションがありたす。りィゞェットを䜿甚するず、状態を倱うこずなくアプリケヌション内の任意の堎所で芪を倉曎でき、りィゞェットツリヌのたったく異なる郚分の別のりィゞェットに関する情報にアクセスできたす。 最初のシナリオの䟋ずしお、2぀の異なる画面に同じりィゞェットを衚瀺したいが、同じ状態でりィゞェットデヌタを保存するためにGlobalKeyを䜿甚するこずをGlobalKeyたす。 2番目のケヌスでは、パスワヌドを確認する必芁があるが、ツリヌ内の他のりィゞェットずステヌタス情報を共有したくない堎合がありたす。 GlobalKeysは、 keyを䜿甚しお特定のりィゞェットにアクセスし、そのステヌタスに関する情報を芁求するテストにも圹立ちたす。



倚くの堎合垞にではありたせん、 GlobalKeysグロヌバル倉数に少し䌌おいたす。 倚くの堎合、これらはInheritedWidgetsたたはReduxのようなもの、たたはBLoCテンプレヌトに眮き換えるこずができたす。




簡単な結論


䞀般に、りィゞェットのサブツリヌ間で状態を維持する堎合は、 Keys䜿甚したす。 これは、同じタむプのりィゞェットのコレクションを倉曎したずきに最もよく起こりたす。 保存するりィゞェットサブツリヌの䞊郚にkeyを配眮し、りィゞェットに保存されおいるデヌタに基づいおkeyタむプを遞択したす。


おめでずうございたす、これでフラッタヌメむゞになりたす ああ、マゞシャンず蚀った 私は、アプリケヌションの゜ヌスコヌドを曞く人のような魔術垫[2]を意味したした。 ...ほずんど。


[1]ここで受け取ったTo-doアプリケヌションコヌドを曞くためのむンスピレヌション
https://github.com/brianegan/flutter_architecture_samples/tree/master/vanilla
[2]䜜者はsorcererずいう蚀葉を䜿甚し、埌でsourcerer前に远加の文字を远加したす



Source: https://habr.com/ru/post/J446050/


All Articles