Flutter 中 key 的原理及作用

語言: CN / TW / HK

這是我參與11月更文挑戰的第13天,活動詳情檢視:2021最後一次更文挑戰

Key 的原理

image.png

image.png

static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; }

如圖 1 所示,當我們生成一個 Widget 樹的時候也會對應生成 Element 樹,WidgetElement 一一對應。但是當我們移除 Widget1 的時候會呼叫 ElementcanUpdate 方法,canUpdate 方法中會判斷舊的 Widget 與新的 WidgetruntimeType 是否相等,且 key 是否相等。當我們不使用 key 時候,就會預設它們相等,就會如圖 2 所示,Element1 就會比較 oldWidget.runtimeType == newWidget.runtimeType,因為 Element1 曾經指向的型別與 Widget2 型別相同所以 Widget2 就會複用 Element1,同理 Widget3 就會複用 Element2,依次比較,當 Element3 沒有指向的時候就會被移除。所以 Key 的作用可以用來跟 Widget 做繫結,判斷 canUpdate 是否執行。

  • Key 本身是一個抽象類,有一個工廠程構造方法 ValueKey()
  • 直接子類主要有:LocalKeyGlobalKey
  • LocalKey 是增量演算法的核心,決定哪個 Element 要保留,哪個 Element 要刪除。以下是 LocalKey 的三個子類。
  • ValueKey:以值作為引數(數字、字串)
  • ObjectKey:以物件作為引數
  • UniqueKey:建立唯一標識
  • GlobalKey 對應某一個 Widget 或者 State 或者 Element

GlobalKey 的使用

``` class GlobalKeyDemo extends StatelessWidget { final GlobalKey<_ChildPageState> _globalKey = GlobalKey();

GlobalKeyDemo({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('GlobalKeyDemo'), ), body: ChildPage(key: _globalKey,), floatingActionButton: FloatingActionButton( onPressed: () { // _globalKey.currentContext // _globalKey.currentWidget _globalKey.currentState?.setState(() { _globalKey.currentState?.data = '666'; _globalKey.currentState?.count++; }); }, child: const Icon(Icons.add), ), ); } }

class ChildPage extends StatefulWidget { const ChildPage({Key? key}) : super(key: key);

@override _ChildPageState createState() => _ChildPageState(); }

class _ChildPageState extends State { int count = 0; String data = 'hello'; @override Widget build(BuildContext context) { return Center( child: Column( children: [ Text(count.toString()), Text(data) ], ), ); } } ```

如案例所示,當 FloatingActionButtononPressed 閉包函式執行的時候我們想修改 _ChildPageStatecountdata 的值,我們可以在 GlobalKeyDemo 中定義 _globalKey = GlobalKey(),在初始化 ChildPage 的時候把 _globalKey 作為引數傳遞。這時候我們可以通過 _globalKey.currentContext_globalKey.currentWidget_globalKey.currentState 獲取我們想拿到的部件,這裡我們通過 _globalKey.currentState就能獲取到 _ChildPageState 修改 datacount 屬性,並呼叫 setState 方法。個人感受 Flutter 中的 GlobalKey 有點類似 iOSUIViewtag 屬性,可以通過 tag 來獲取到對應的 UIView 控制元件。