Element複用:StatefulWidget修改和key

在Flutter中,Widget的功能是“描述一個UI元素的配置數據”,即,Widget其實並不是表示最終繪製在設備屏幕上的顯示元素,而只是顯示元素的一個配置數據。Flutter中真正代表屏幕上顯示元素的類是Element
若類比於編程語言,Widget就像是一個抽象類,而Element纔是具體的類實例。
因此,一個Widget對象可能會對應多個Element對象。

渲染流程

  1. 根據用戶代碼創建Widget樹。
  2. 由Widget樹創建對應的Element樹。
  3. 由Element樹生成Render樹。

最後,Render樹經過佈局、繪製等,在界面上顯示出來。
由於具體的顯示都是Element控制,因此可以認爲Element是顯示在界面上的元素。

Element的複用

出於效率原因,Element是可複用的。
對於新的一幀,Widget會判斷每個Element是否可複用。若是,則保留Element,並用新的Widget配置來更新;否則,則創建新的Element。
也就是說,對於一幀新畫面上的Element,要麼更新,要麼重建。
Widget負責判斷Element是否可複用。Widget的判斷源碼爲:

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

判斷條件很簡單,根據前後幀的兩個Widget配置:

  1. runtimeType相等
  2. key相等

若兩個條件都爲true,則表示Element可複用,只使用newWidget來更新Element即可;否則,則表示Element不可複用,需直接創建。
其中:

  1. runtimeType爲運行時類型,即該Widget的類。例如,1/2/3的runtimeType爲int,“1”/“2”/"3"的runtimeType爲String,自定義的Widget其runtimeType爲具體的自定義類型。僅僅指類型,不涉及具體的數據。
  2. key即用戶傳入的key。若用戶沒有傳入,則爲null。(null == null)返回true。

實例: StatelessWidget

定義一個StatelessWidget:

class StatelessW extends StatelessWidget {
  String text = '';

  StatelessW(this.text, {Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(text);
  }
}

如上,該StatelessW接收1個父組件傳入的String。
進行如下操作:

  1. StatelessW sw = StatelessW(‘text1’),並放在某個容器中。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text1’。
    • 該Widget對應的Element不存在,故會創建新的Element。

    此時,界面上顯示文本’text1’。

  2. sw = StatelessW(‘text2’)。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text2’。

    • 該Widget對應的Element存在,於是進入canUpdate()判斷:

      • oldWidget.runtimeType爲StatelessW,newWidget.runtimeType爲StatelessW,相等
      • oldWidget.key爲null,newWidget.key爲null,相等

      於是,Element判定爲可複用,保留。

    • 使用newWidget來更新複用的Element。由於newWidget中text = ‘text2’,故Element要顯示的text屬性會被更新爲’text2’。

    此時,界面上顯示文本’text2’。

  3. sw = StatelessW(‘text3’, key: UniqueKey())。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text3’。

    • 該Widget對應的Element存在,於是進入canUpdate()判斷:

      • oldWidget.runtimeType爲StatelessW,newWidget.runtimeType爲StatelessW,相等
      • oldWidget.key爲null,newWidget.key爲一個隨機值,不相等

      於是,Element判定爲可不復用,需創建新的Element。

    • 創建新的Element。由於newWidget中text = ‘text3’,故新的Element中要顯示的text屬性是’text3’。

    此時,界面上顯示文本’text3’。

實例: StatefulWidget

定義一個StatefulWidget:

class StatefulW extends StatefulWidget {
  String text = '';

  StatefulW(this.text, {Key key}) : super(key: key);

  @override
  _StatefulWState createState() => _StatefulWState(text);
}

class _StatefulWState extends State<StatefulW> {
  String sText = '';

  _StatefulWState(this.text) {
    this.sText = widget.text;
  }

  @override
  Widget build(BuildContext context) {
    return Text(sText);
  }
}

如上,該StatefulW接收1個父組件傳入的String。
進行如下操作:

  1. StatefulW sw = StatefulW(‘text1’),並放在某個容器中。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text1’。
    • 該Widget對應的Element不存在,故會創建新的Element。
    • Element創建時,內部的_StatefulWState會一同創建。_StatefulWState內部定義了一個sText,創建時會被賦予初值’text1’。

    顯示依賴的是_StatefulWState。此時,界面上顯示文本’text1’。

  2. sw = StatefulW(‘text2’)。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text2’。

    • 該Widget對應的Element存在,於是進入canUpdate()判斷:

      • oldWidget.runtimeType爲StatefulW,newWidget.runtimeType爲StatefulW,相等
      • oldWidget.key爲null,newWidget.key爲null,相等

      於是,Element判定爲可複用,保留。其內部的_StatefulWState也隨之保留。

    • 使用newWidget來更新複用的Element。由於newWidget中text = ‘text2’,故Element的text屬性會被更新爲’text2’。

    • 然而,_StatefulWState被保留,故不會再次進入構造函數。於是_StatefulWState中的sText屬性依然爲’text1’。

    顯示依賴的是_StatefulWState。此時,儘管StatefulW的text屬性已經更新爲’text2’,但_StatefulWState中的sText屬性依然爲’text1’,故界面上顯示文本’text1’。

  3. sw = StatefulW(‘text3’, key: UniqueKey())。框架的對應邏輯爲:

    • 爲Widget設置數據text = ‘text3’。

    • 該Widget對應的Element存在,於是進入canUpdate()判斷:

      • oldWidget.runtimeType爲StatefulW,newWidget.runtimeType爲StatefulW,相等
      • oldWidget.key爲null,newWidget.key爲一個隨機值,不相等

      於是,Element判定爲可不復用,需創建新的Element。

    • 創建新的Element。由於newWidget中text = ‘text3’,故新的Element中text屬性是’text3’。

    • Element創建時,內部的_StatefulWState會一同創建。_StatefulWState內部定義了一個sText,創建時會被賦予初值’text3’。

    顯示依賴的是_StatefulWState。此時,界面上顯示文本’text3’。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章