【老孟Flutter】為什麼 build 方法放在 State 中而不是在 StatefulWidget 中
2021-01-13 22:00:49 軟體

老孟導讀:此篇文章是生命週期相關文章的番外篇,在檢視原始碼的過程中發現了這一有趣的問題,歡迎大家一起探討。

Flutter 中Stateful 元件的生命週期:http://laomengit.com/blog/20201227/Stateful%E7%BB%84%E4%BB%B6%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html

Flutter 中與平臺相關的生命週期:http://laomengit.com/blog/20201227/%E7%9B%B8%E5%85%B3%E5%B9%B3%E5%8F%B0%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.html

部落格中還有更多精彩文章,也歡迎加入 Flutter 交流群。

靈活性

build 方法放在 State 中比放在 StatefulWidget 中更具靈活性,比如說,AnimatedWidgetStatefulWidget 的子類,AnimatedWidget 是一個抽象類,其中有一個 Widget build(BuildContext context) 的抽象方法,此方法需要子類重寫,AnimatedWidget 原始碼如下:

abstract class AnimatedWidget extends StatefulWidget {
  ...
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
  @protected
  Widget build(BuildContext context);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

  ...
}

刪除了一些程式碼,保留了重點程式碼。

試想一下,如果 build 方法放在 StatefulWidget 中,則 AnimatedWidget 中的 build 方法需要帶一個 State 引數,如下:

abstract class AnimatedWidget extends StatefulWidget {
  ...
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
  @protected
  Widget build(BuildContext context, AnimatedState state);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

  ...
}

但 AnimatedState 是內部實現,並不需要開放給外部(開發者),外部也不需要知道 AnimatedState 的內部實現。

閉包 this 指向異常

假設 build 方法在 StatefulWidget 中,StatefulWidget 的子類寫法如下:

class MyWidget extends StatefulWidget {

  final Color color;

  @override
  Widget build(BuildContext context, MyWidgetState state) {
    print('${this.color}');
    return Container();
  }
}

此時的 this 指向的是 MyWidget 的範例,然後父元件改變顏色,重新構建 MyWidget 元件,前一個 MyWidget 的範例中的 this 依然指向前一個 MyWidget 的範例,顏色並未發生變化。

如果 build 方法在 State 中,程式碼如下:

class MyWidget extends StatefulWidget {
  final Color color;

  const MyWidget({Key key, this.color}) : super(key: key);
  
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    print('${widget.color}');
    return Container();
  }
}

同樣,父元件改變顏色,重新構建 MyWidget 元件,此時框架更新了 State 物件的 widget 屬性的參照,新的 MyWidget 範例和 $ {widget.color} 將顯示綠色。

效能

有狀態的元件包含StatefulWidget 和 State,當有狀態元件的設定發生更改時,StatefulWidget 將會被丟棄並重建,而 State 不會重建,框架會更新 State 物件中 widget 的參照,這極大的減輕了系統重建有狀態元件的工作。

此方式對動畫來說極為重要,由於 State 不會被重建,保留了前面的狀態,不斷的根據前一個狀態計算下一個狀態並重建其widget,達到動畫的效果。

交流

老孟Flutter部落格(330個控制元件用法+實戰入門系列文章):http://laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】: