フラッターを理解する上での鍵

概要

Flutter途中、我々はすべてのインターフェイスのビューを更新する方法を知っている:修正することによってStataトリガするためにWidget再構成を、トリガーと更新操作はFlutter行うためのフレームワーク。しかし、時には修正StateFlutterフレームワークにはトリガしないように見えるWidget復興、
暗示されるFlutterの枠組み内メカニズムを更新するために、いくつかのケースでの使用の組み合わせを必要としKey、実際のトリガするために、「復興を。」
ここでは三つの側面(とき、どの)から、合理的な時間と場所で、合理的なキーを使用する方法について説明します。

とき:するときに使用します Key

実際の例

要件:画面上のボタンをクリックし、ラインの色の二つのブロックを交換します。

達成StatelessWidget

使用StatelessWidgetStatelessColorfulTileやります)childtiles):

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

PositionedTiles

需要の実装を成功^ _ ^

達成StatefulWidget

使用StatefulWidgetStatefulColorfulTileやります)childtiles):

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(),
    ];
  }
結果

PositionedTiles

一見何の影響ありません_

なぜStatefulWidget正常に更新することはできませんか?あなたは次のことを理解する必要があります。

原則ウィジェットのFluuter更新

?フラッターの枠組みでは、ツリーの維持の観点から、我々は最終的にツリーにまとめ巣ウィジェットを用意しました。

StatelessWidget

最初の使用ではStatelessWidget実現の、フラッターは、これらのウィジェットをレンダリングするとき、Rowウィジェットはその子ウィジェット用のスロットの順序付きセットを提供します。各ウィジェットについて、フラッターは、対応する構築しますElementこの建設Elementツリーが非常に簡単です、唯一それぞれについて、保存Widgetする情報の種類だけでなく、サブWidget参照。あなたは、この使用することができElementますが、フラッターのApp骨格を好きなようにツリーを。これは、Appの構造を示しているが、元の参照によって必要なその他の情報はWidget見つけること。

StatelessWidgetツリー&エレメントツリー

私たちは色のブロックの2つの行を交換するときは、フラッターはトラバースWidget同じ骨格構造かどうかを確認するために木を。それからあるRowウィジェット開始、その後、その子ウィジェットに移動し、要素古い木は、ウィジェットウィジェットと同じタイプかどうかを確認してくださいKeyあなたが同じであれば、それは新しいウィジェットへの参照を更新します。ここでは、ウィジェットは、キーを設定し、そうではないFlutterだけの種類を確認してください。それは同じことを行うために第2子です。したがって、要素ツリーは、対応するウィジェットツリーに応じて更新されます。

スワップした後、

更新が要素ツリー、フラッターは要素ツリーに従ってレンダリングオブジェクトツリーを構築します完了すると、最終的にレンダリングプロセスを開始しました。

このレンダリング処理と同様に

StatefulWidget

ときに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()),
  ];
}

再次运行:

PositionedTiles

成功 swap!

添加了 Key 之后的结构:

PositionedTiles

当现在执行 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()),
    ),
  ];
}

ときに、Exchange

当点击按钮发生交换之后,可以看到两个色块的颜色会随机改变,但是我的预期是两个固定的颜色彼此交换。

为什么产生问题

当Widget 树中两个 Padding 发生了交换,它们包裹的色块也就发生了交换:

為替

然后 Flutter 将进行检查,以便对 Element 树进行对应的更新: Flutter 的 Elemetn to Widget 匹配算法将一次只检查树的一个层级:

検査

  1. 在第一级,Padding Widget 都正确匹配。

検査

  1. 在第二级,Flutter 注意到 Tile 控件的 Key 不匹配,就停用该 Tile Element,删除 Widget 和 Element 之间的连接

検査

  1. 我们这里使用的 KeyUniqueKey, 它是一个 LocalKey

LocalKey 的意思是: 当 Widget 与 Element 匹配时,Flutter 只在树中特定级别内查找匹配的 Key。因此 Flutter 无法在同级中找到具有该 Key 的 Tile Widget,所以它会创建一个新 Element 并初始化一个新 State。 就是这个原因,造成色块颜色发生随机改变,每次交换相当于生成了两个新的 Widget。

  1. 解决这个问题: 将 Key 设置到上层 Widget Padding

当 Widget 树中两个 Padding 发生交换之后,Flutter 就能根据 PaddingKey 的变化,更新 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> 持有一个表单 FormState。 Flutter.dev 上有这个例子Building a form with validation

実際にGlobalKeysはビットのグローバル変数のように見えます。また、役割GlobalKeys、このようInheritedWidget、Reduxのか、ブロックパターンを達成するために、他のより良い方法があります。

概要

どのように合理的かつ適切な使用Key

  1. とき:あなたは、ステータスウィジェットツリーを維持したい場合は、使用Key例えば:変更ウィジェットの同じタイプが設定されている場合(例えば、リスト)
  2. ここで、Key上部の設定は、ウィジェットツリーのユニークなIDを指定します
  3. これ:ウィジェットに格納されるデータの種類に応じて使用の異なるタイプを選択します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

おすすめ

転載: www.cnblogs.com/jiy-for-you/p/11707366.html