banner
jzman

jzman

Coding、思考、自觉。
github

Flutter系列之Widget生命週期

PS:你的計劃很完美,但世界變化是在太快。

上篇文章學習了 Flutter 中的圖片加載及源碼分析,做過移動端開發的朋友都知道組件的生命週期,Flutter 中也是一樣,了解和學習好 Flutter 中的組件的生命週期非常重要,同系列文章如下:

本篇文章將主要介紹 Flutter 中 Widget 的生命週期,具體如下:

  1. StatelessWidget
  2. StatefulWidget
  3. State 生命週期狀態
  4. State 生命週期方法  

StatelessWidget#

StatelessWidget 派生的組件是無狀態組件,無狀態組件只是在構建的時候渲染一次,不支持動態變化,即無法通過其他用戶操作重繪組件,只能接收傳入時的參數進行構建,如下 :

/// StatelessWidget
/// 表示無狀態Widget
class StatelessSamplePage extends StatelessWidget {

  // 外部傳入數據
  final String data;
  StatelessSamplePage(this.data);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.lightBlue,
      child: Text(data),
    );
  }
}

如上傳入的參數只能使用 final 修飾,否則會出現如下警告:

This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: StatelessSamplePage.data

提示 Widget 被 @immutable 註解修飾,如下:

@immutable
abstract class Widget extends DiagnosticableTree {

此時只能使用 final 來修飾變量,Dart 中被 final 修飾的變量只能被初始化一次,這也符合 StatelessWidget 的無狀態特徵。

StatefulWidget#

StatefulWidget 派生的組件是有狀態組件,有狀態的組件是支持隨著數據變化多次構建 Widget 以完成動態界面的渲染,如果要實現一個實時顯示當前時間的界面,顯然 StatelessWidget 是不能完成的,只能使用有狀態的 StatefulWidget 來實現,如下:

/// StatefulWidget
/// 表示有狀態的 Widget
class StatefulSamplePage extends StatefulWidget {
  @override
  _StatefulSamplePageState createState() => _StatefulSamplePageState();
}

class _StatefulSamplePageState extends State<StatefulSamplePage> {
  DateFormat _dateFormat;
  String _currentTime;
  Timer _timer;

  @override
  void initState() {
    super.initState();
    _dateFormat = new DateFormat("yyyy-MM-dd HH:mm:ss");
    // 初始化當前時間
    _currentTime = _dateFormat.format(DateTime.now());
    // 更新時間
    _startTime();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Scaffold(
        appBar: AppBar(
          title: Text("Stateful Widget sample"),
          centerTitle: true,
        ),
        body: Align(
          alignment: Alignment.topCenter,
          child: Text("當前時間:$_currentTime"),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    if(_timer!=null){
      _timer.cancel();
    }
  }

  _startTime() {
    const Duration duration = Duration(seconds: 1);
    _timer = Timer.periodic(
        duration,
        (Timer timer) => {
              // 刷新界面狀態
              setState(() {
                _currentTime = _dateFormat.format(DateTime.now());
              })
            });
  }
}

效果如下:

image

實際上,如果只是靜態界面 StatelessWidget 和 StatefulWidget 是完全沒有區別的,都是能夠實現的,唯一区別就是 StatefulWidget 可以通過 setState 方法觸發 Widget 的重新構建, State 類就是就是 Stateless -> Stateful 的橋樑。

State 生命週期狀態#

Flutter 生命週期實際上各個組件的生命週期:

  • StatelessWidget:無狀態組件的生命週期只有 build 構建這個過程;
  • StatefulWidget:無狀態組件的生命週期指的是 State 的生命週期。

Flutter 生命週期實際上就是無狀態組件的生命週期,即 State 的生命週期,如下圖所示:

image

如上各個 State 的生命週期狀態主要是三個:

  • created:指的是 State 的創建狀態,當調用 createState 方法之後就處於 created state;
  • dirty:指的是當調用 setState 等方法數據發生變化,但是 Widget 還未重新構建時的狀態;
  • clean:指的是 Widget 構建後的狀態;
  • defunct:指的是 State.dispose 調用之後的狀態,此時對應的 Widget 已被銷毀不能夠被再次構建。

State 生命週期方法#

有狀態組件的生命週期就是 State 的生命週期,其具體調用過程與 build 觸發時機如下圖所示:

image

其生命週期方法具體含義如下:

  • createState:StatefulWidget 中用於創建 State;
  • initState:State 的初始化操作,如變量的初始化等;
  • didChangeDependencies:initState 調用之後調用,或者使用了 InheritedWidgets 組件會被調用,其中 InheritedWidgets 可用於 Flutter 狀態管理;
  • build:用於 Widget 的構建;
  • deactivate:包含此 State 對象的 Widget 被移除之後調用,若此 Widget 被移除之後未被添加到其他 Widget 樹結構中,則會繼續調用 dispose 方法;
  • dispose:該方法調用後釋放 Widget 所佔資源;
  • reassemble:用於開發階段,熱重載的時候會被調用,之後會重新構建;
  • didUpdateWidget:父 Widget 構建的時候子 Widget 的 didUpdateWidget 方法會被調用。

在對應的 Widget 生命週期方法中加上日誌,驗證上述生命週期方法的執行,父 Widget 源碼如下:

const String TAG = "Flutter";
/// Widget生命週期
class WidgetLifecycleSamplePage extends StatefulWidget {
  @override
  _WidgetLifecycleSamplePageState createState() {
    Log.info(TAG, "parent createState");
    return _WidgetLifecycleSamplePageState();
  }
}

class _WidgetLifecycleSamplePageState extends State<WidgetLifecycleSamplePage> {

  num _count = 0;

  @override
  void initState() {
    super.initState();
    Log.info(TAG, "parent initState");
  }

  @override
  Widget build(BuildContext context) {
    Log.info(TAG, "parent build");
    return Scaffold(
      appBar: AppBar(
        title: Text("Widget Lifecycle Sample"),
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          Center(
            child: RaisedButton(
                textColor:Colors.white,
                color: Colors.lightBlue,
                child: Text("parent->setState:$_count",style: TextStyle(color: Colors.white),),
                onPressed: (){
                  setState(() {
                    Log.info(TAG, "parent setState");
                    _count++;
                  });
                }),
          ),
          SubWidgetLifecycleSamplePage(),
        ],
      )
    );
  }

  @override
  void didUpdateWidget(WidgetLifecycleSamplePage oldWidget) {
    super.didUpdateWidget(oldWidget);
    Log.info(TAG, "parent didUpdateWidget");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    Log.info(TAG, "parent didChangeDependencies");
  }

  @override
  void deactivate() {
    super.deactivate();
    Log.info(TAG, "parent deactivate");
  }

  @override
  void reassemble() {
    super.reassemble();
    Log.info(TAG, "parent reassemble");
  }

  @override
  void dispose() {
    super.dispose();
    Log.info(TAG, "parent dispose");
  }
}

/// 子Widget
class SubWidgetLifecycleSamplePage extends StatefulWidget {
  @override
  _SubWidgetLifecycleSamplePageState createState() {
    Log.info(TAG, "sub createState");
    return _SubWidgetLifecycleSamplePageState();
  }
}

其中子 Widget 實現與父 Widget 實現相似就不貼了,可在公眾號後台回復關鍵字【Lifecycle】獲取完成源碼,上述代碼顯示效果如下:

image

其對應的生命週期方法調用如下:

image

分析如下:

  1. Widget 初始化流程指的是進入這個 Widge 所在頁面的生命週期流程,先父 Widget 後子 Widget,然後都依次調用 createState、initState、didChangeDepandencies、build 方法;
  2. 當父 Widget seteState 更新數據時觸發了父 Widget 的構建,故子 Widget 調用了 didUpdateWidget 方法;
  3. 其他流程比如熱重載、Widget 銷毀都與上面的 State 生命週期方法流程圖相同,這裡不再贅述。
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。