概要
でFlutter
途中、我々はすべてのインターフェイスのビューを更新する方法を知っている:修正することによってStata
トリガするためにWidget
再構成を、トリガーと更新操作はFlutter
行うためのフレームワーク。しかし、時には修正State
、Flutter
フレームワークにはトリガしないように見えるWidget
復興、
暗示されるFlutter
の枠組み内メカニズムを更新するために、いくつかのケースでの使用の組み合わせを必要としKey
、実際のトリガするために、「復興を。」
ここでは三つの側面(とき、どの)から、合理的な時間と場所で、合理的なキーを使用する方法について説明します。
とき:するときに使用します Key
実際の例
要件:画面上のボタンをクリックし、ラインの色の二つのブロックを交換します。
達成StatelessWidget
使用StatelessWidget
(StatelessColorfulTile
やります)child
(tiles
):
class PositionedTiles extends StatefulWidget {
@override
State<StatefulWidget> createState() => PositionedTilesState();
}
class PositionedTilesState extends State<PositionedTiles> {
List<Widget> tiles;
@override
void initState() {
super.initState();
tiles = [
StatelessColorfulTile(),
StatelessColorfulTile(),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: tiles))),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles));
}
あなたがボタンをクリックすると、更新するために、PositionedTilesState
保存されましたtiles
:
void swapTiles() {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
}
}
class StatelessColorfulTile extends StatelessWidget {
final Color color = UniqueColorGenaretor.getColor();
StatelessColorfulTile({Key key}) : super(key: key);
@override
Widget build(BuildContext context) => buildColorfulTile(color);
}
結果
達成StatefulWidget
使用StatefulWidget
(StatefulColorfulTile
やります)child
(tiles
):
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => StatefulColorfulTileState();
}
class StatefulColorfulTileState extends State<StatefulColorfulTile> {
// 将 Color 储存在 StatefulColorfulTile 的 State StatefulColorfulTileState 中.
Color color;
@override
void initState() {
super.initState();
color = UniqueColorGenaretor.getColor();
}
@override
Widget build(BuildContext context) => buildColorfulTile(color);
}
外容器変更PositionedTiles
のをtiles
:
@override
void initState() {
super.initState();
tiles = [
StatefulColorfulTile(),
StatefulColorfulTile(),
];
}
結果
なぜStatefulWidget
正常に更新することはできませんか?あなたは次のことを理解する必要があります。
原則ウィジェットのFluuter更新
?フラッターの枠組みでは、ツリーの維持の観点から、我々は最終的にツリーにまとめ巣ウィジェットを用意しました。
StatelessWidget
最初の使用ではStatelessWidget
実現の、フラッターは、これらのウィジェットをレンダリングするとき、Row
ウィジェットはその子ウィジェット用のスロットの順序付きセットを提供します。各ウィジェットについて、フラッターは、対応する構築しますElement
。この建設Element
ツリーが非常に簡単です、唯一それぞれについて、保存Widget
する情報の種類だけでなく、サブWidget
参照。あなたは、この使用することができElement
ますが、フラッターのApp骨格を好きなようにツリーを。これは、Appの構造を示しているが、元の参照によって必要なその他の情報はWidget
見つけること。
私たちは色のブロックの2つの行を交換するときは、フラッターはトラバースWidget
同じ骨格構造かどうかを確認するために木を。それからあるRow
ウィジェット開始、その後、その子ウィジェットに移動し、要素古い木は、ウィジェットウィジェットと同じタイプかどうかを確認してくださいKey
。あなたが同じであれば、それは新しいウィジェットへの参照を更新します。ここでは、ウィジェットは、キーを設定し、そうではないFlutter
だけの種類を確認してください。それは同じことを行うために第2子です。したがって、要素ツリーは、対応するウィジェットツリーに応じて更新されます。
更新が要素ツリー、フラッターは要素ツリーに従ってレンダリングオブジェクトツリーを構築します完了すると、最終的にレンダリングプロセスを開始しました。
StatefulWidget
ときにStatefulWidget
コントロールツリーの構造を実現類似しているが、今の色情報は、制御自体に格納されていない、むしろ状態で外部物体。
现在,我们点击按钮,交换控件的次序,Flutter 将遍历 Element 树,检查 Widget 树中 Row
控件并且更新 Element 树中的引用,然后第一个 Tile 控件检查它对应的控件是否是相同类型,它发现对方是相同的类型; 然后第二个 Tile 控件做相同的事情,最终就导致 Flutter 认为这两个控件都没有发生改变。Flutter 使用 Element 树和它对应的控件的 State 去确定要在设备上显示的内容, 所以 Element 树没有改变,显示的内容也就不会改变。
StatefullWidget 结合 Key
现在,为 StatefulColorfulTile
传递一个 Key
对象:
void initState() {
super.initState();
tiles = [
// 使用 UniqueKey
StatefulColorfulTile(key: UniqueKey()),
StatefulColorfulTile(key: UniqueKey()),
];
}
再次运行:
成功 swap!
添加了 Key
之后的结构:
当现在执行 swap 时, Element 数中 StatafulWidget 控件除了比较类型外,还会比较 key
是否相等:
只有类型和key
都匹配时,才算找到对应的 Widget。于是在 Widget Tree 发生交换后,Element Tree 中子控件和原始控件对应关系就被打乱了,所以 Flutter 会重建 Element Tree,直到控件们正确对应上。
所以,现在 Element 树正确更新了,最终就会显示交换后的色块。
使用场景
如果要修改集合中的控件的顺序或数量,Key
会很有用。
Where: 在哪设置 Key
正常情况下应该在当前 Widget 树的顶级 Widget 中设置。
回到 StatefulColorfulTile
例子中,为每个色块添加一个 Padding
,同时 key
还是设置在相同的地方:
@override
void initState() {
super.initState();
tiles = [
Padding(
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
];
}
当点击按钮发生交换之后,可以看到两个色块的颜色会随机改变,但是我的预期是两个固定的颜色彼此交换。
为什么产生问题
当Widget 树中两个 Padding
发生了交换,它们包裹的色块也就发生了交换:
然后 Flutter 将进行检查,以便对 Element 树进行对应的更新: Flutter 的 Elemetn to Widget
匹配算法将一次只检查树的一个层级:
- 在第一级,
Padding
Widget 都正确匹配。
- 在第二级,Flutter 注意到 Tile 控件的
Key
不匹配,就停用该 Tile Element,删除 Widget 和 Element 之间的连接
- 我们这里使用的
Key
是UniqueKey
, 它是一个LocalKey
LocalKey
的意思是: 当 Widget 与 Element 匹配时,Flutter 只在树中特定级别内查找匹配的 Key。因此 Flutter 无法在同级中找到具有该 Key 的 Tile Widget,所以它会创建一个新 Element 并初始化一个新 State。 就是这个原因,造成色块颜色发生随机改变,每次交换相当于生成了两个新的 Widget。
- 解决这个问题: 将
Key
设置到上层 WidgetPadding
上
当 Widget 树中两个 Padding
发生交换之后,Flutter 就能根据 Padding
上 Key
的变化,更新 Element
树中的两个 Padding
,从而实现交换。
@override
void initState() {
super.initState();
tiles = [
Padding(
key: UniqueKey(),
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(),
),
Padding(
key: UniqueKey(),
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(),
),
];
}
Which: 该使用哪种类型的 Key
Key
的目的在于为每个 Widget 指明一个唯一的身份,使用何种 Key
就要依具体的使用场景决定。
- ValueKey
例如在一个 ToDo
列表应用中,每个 Todo
Item 的文本是恒定且唯一的。这种情况,适合使用 ValueKey
,value 是文本。
- ObjectKey
假设,每个子 Widget 都存储了一个更复杂的数据组合,比如一个用户信息的地址簿应用。任何单个字段(如名字或生日)可能与另一个条目相同,但每个数据组合是唯一的。在这种情况下, ObjectKey
最合适。
- UniqueKey
如果集合中有多个具有相同值的 Widget,或者如果您想确保每个 Widget 与其他 Widget 不同,则可以使用 UniqueKey
。 在我们的例子中就使用了 UniqueKey
,因为我们没有将任何其他常量数据存储在我们的色块上,并且在构建 Widget 之前我们不知道颜色是什么。
不要在 Key
中使用随机数,如果你那样设置,那么当每次构建 Widget 时,都会生成一个新的随机数,Element 树将不会和 Widget 树做一致的更新。
- GlobalKeys
Global Keys有两种用途。
- 它们允许 Widget 在应用中的任何位置更改父级而不会丢失 State ,或者可以使用它们在 Widget 树 的完全不同的部分中访问有关另一个 Widget 的信息。
- 比如: 要在两个不同的屏幕上显示相同的 Widget,同时保持相同的 State,则需要使用 GlobalKeys。
在第二种情况下,您可能希望验证密码,但不希望与树中的其他 Widget 共享该状态信息,可以使用
GlobalKey<FromState>
持有一个表单Form
的State
。 Flutter.dev 上有这个例子Building a form with validation。
実際にGlobalKeysはビットのグローバル変数のように見えます。また、役割GlobalKeys、このようInheritedWidget、Reduxのか、ブロックパターンを達成するために、他のより良い方法があります。
概要
どのように合理的かつ適切な使用Key
:
- とき:あなたは、ステータスウィジェットツリーを維持したい場合は、使用
Key
。例えば:変更ウィジェットの同じタイプが設定されている場合(例えば、リスト) - ここで、
Key
上部の設定は、ウィジェットツリーのユニークなIDを指定します - これ:ウィジェットに格納されるデータの種類に応じて使用の異なるタイプを選択します
Key
参照
- https://flutter.dev/docs/development/ui/widgets-intro#keys
- https://api.flutter.dev/flutter/foundation/Key-class.html
- https://www.youtube.com/watch?v=kn0EOS-ZiIc
- https://www.yuque.com/xytech/flutter/tge705
上記で言及コードの例:https://github.com/stefanJi/fullter-playgroud