1、Flutter Tips - Widget Key;

Melrose發表於2019-04-01

  當WidgetWidget Tree中移動時,Key會保留其狀態。它們可用於保留使用者的滾動位置等資訊。

Key的種類

   Key主要分為Local KeysGlobal Keys;

Local Keys

  • ValueKey : ValueKey('value')
  • ObjectKey : ObjectKey(MutableRectangle(1,2,3,4))
  • UniqueKey : UniqueKey()
  • PageStorageKey : PageStorageKey(scrollLocation)

Global Keys

  • GlobalKey : GlobalKey()

什麼時候使用Key

  大多數情況下,我們並不需要使用Key。但是如果你發現自己需要新增、刪除或者重新排序處於某種狀態的相同型別的Widget集合就會用到Key

import 'package:flutter/material.dart';

class KeyDemoApp extends StatelessWidget{

  @override
  Widget build(BuildContext context) => MaterialApp(
    home: _HomePage(),
  );
}


class _HomePage extends StatefulWidget{
  @override
  State<StatefulWidget> createState() =>_HomePageState();
}

class _HomePageState extends State<_HomePage>{
  List<Widget> _widgets;

  @override
  void initState() {
    super.initState();
    _widgets= [
        RectStateless(Color.fromARGB(255, 0, 0, 0)),
        RectStateless(Color.fromARGB(255, 255, 0, 0))
    ];
  }
  @override
  Widget build(BuildContext context) => Scaffold(
    body:  Center(
      child: Row(children: _widgets,),
    ),
    floatingActionButton: FloatingActionButton(onPressed: swapWidget),
  );


  void swapWidget(){
    setState(() {
      _widgets.insert(1, _widgets.removeAt(0));
    });
  }
}

class RectStateless extends StatelessWidget{
  final Color _bg;
  RectStateless(this._bg,[Key key]):super(key:key);

  @override
  Widget build(BuildContext context) {
    return  Container(color:_bg,
      width: 100,
      height: 100,
    );
  }
}

複製程式碼

  在上面的程式碼中,如果點選按鈕則兩個Widget可以自由的更換位置。如果將兩個Widget更換為StatefulWidgetWidget後:

class RectStateful extends StatefulWidget{
 final Color _bg;
 RectStateful(this._bg,[Key key]):super(key:key);
 @override
 State<StatefulWidget> createState() =>RectStatefulState(_bg);

}

class RectStatefulState extends State<RectStateful>{
 final Color _bg;
 RectStatefulState(this._bg);

 @override
 Widget build(BuildContext context) {
   return  Container(color:_bg,
     width: 100,
     height: 100,
   );
 }
}
複製程式碼

當點選時兩個色塊都不會交換,當新增上Key後則又可以交換位置:

_widgets= [
 RectStateful(Color.fromARGB(255, 0, 0, 0),UniqueKey()),
 RectStateful(Color.fromARGB(255, 255, 0, 0),UniqueKey())
];
複製程式碼

  從上面的例子可以看出: 如果集合中的整個小部件子樹是無狀態的,則不需要使用Key。

Key的運作方式

  在Stateless的例子中,Row為子Widget提供了一組有序的插槽。對於每個小部件,Flutter都會構建一個相應的Element,Element tree只會儲存每個Widget型別以及對子Element的引用資訊。可以將Element tree視為Flutter應用程式的骨架,它顯示了應用程式的結構。當交換Row中的Widget時,Flutter會遍歷Element tree來檢視骨架結構是否相同,即檢查新的Widget是否和舊的Widget的型別和Key是否相同,如果是一樣的,它會更新對新Widget的引用,對於其他Widget也是對應的步驟。Stateless的例子中,Widget沒有Key,Flutter只會檢查其型別。

  在Stateful沒有Key例子中,每個Widget都會有一個State物件來儲存相應的資訊,這些資訊而不是儲存在小部件本身中。Flutter會檢查Row小部件的型別,型別是一樣的會更新引用,第二個Widget也如此。Flutter會遍歷Element tree以及其對應的State來確定在裝置上顯示的實際內容。因此,看起來相應的Widget並沒有正確的交換。

  在有KeyStateful例子中,向小部件新增來key的屬性。在交換Row小部件時會像之前一樣匹配。但是新的Widget的金鑰和之前小部件Element對應的Key不匹配。因此,Flutter會停用這些Element,從第一個不匹配的Element開始,然後Flutter會檢視不匹配的子項,查詢具有相同金鑰的Element,它會找到匹配項,並更新對相應Widget的引用。然後Flutter會為後續的子項做相同的事情。

相關文章