1. 程式人生 > >flutter中的生命周期函數

flutter中的生命周期函數

點擊 依賴 變量 activate def 經典 import 區域 處理

前言:生命周期是一個組件加載到卸載的整個周期,熟悉生命周期可以讓我們在合適的時機做該做的事情,
flutter中的State生命周期和android以及React Native的生命周期類似。

先看一張生命周期的流程圖:

技術分享圖片
大致可以分為3個階段:

初始化
狀態變化
組件移除
初始化
State初始化時會依次執行 : 構造函數 > initState > didChangeDependencies > Widget build , 此時頁面加載完成。

然後我們看一下每個函數的意義:

構造函數
調用次數:1次

這個函數嚴格意義上來講不屬於生命周期的一部分,因為這個時候State的widget屬性為空,無法在構造函數中訪問widget的屬性 。但是構造函數必然是要第一個調用的。可以在這一部分接收前一個頁面傳遞過來的數據。

initState
Called when this object is inserted into the tree.

調用次數:1次

當插入渲染樹的時候調用,這個函數在生命周期中只調用一次。這裏可以做一些初始化工作,比如初始化State的變量。

didChangeDependencies
Called when a dependency of this [State] object changes.

初始化時,在initState()之後立刻調用
當依賴的InheritedWidget rebuild,會觸發此接口被調用
這個函數會緊跟在initState之後調用,並且可以調用BuildContext.inheritFromWidgetOfExactType,那麽BuildContext.inheritFromWidgetOfExactType的使用場景是什麽呢?最經典的應用場景是

new DefaultTabController(length: 3, child: new TabBar(
      tabs: [ "主頁","訂單","我的" ]
      .map( (data)=>new Text(data) ).toList(),

  

TabBar本來需要定義一個TabController,但是在外面套一層DefaultTabController就不需要定義TabContrller了,看下源碼:

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _updateTabController();
    _initIndicatorPainter();
  }

void _updateTabController() {
    final TabController newController = widget.controller ?? DefaultTabController.of(context);
    ...
    }

  

註意到這裏DefaultTabController.of(context)

static TabController of(BuildContext context) {
    final _TabControllerScope scope = context.inheritFromWidgetOfExactType(_TabControllerScope);
    return scope?.controller;
  }

  

實際上就是調用BuildContext.inheritFromWidgetOfExactType,也就說在didChangeDependencies中,可以跨組件拿到數據。

#運行時

build
調用次數:多次

初始化之後開始繪制界面,當setState觸發的時候會再次被調用

didUpdateWidget
Called whenever the widget configuration changes.

祖先節點rebuild widget時調用 .當組件的狀態改變的時候就會調用didUpdateWidget.

理論上setState的時候會調用,但我實際操作的時候發現只是做setState的操作的時候沒有調用這個方法。而在我改變代碼hot reload時候會調用 didUpdateWidget 並執行 build…

實際上這裏flutter框架會創建一個新的Widget,綁定本State,並在這個函數中傳遞老的Widget。
這個函數一般用於比較新、老Widget,看看哪些屬性改變了,並對State做一些調整。

需要註意的是,涉及到controller的變更,需要在這個函數中移除老的controller的監聽,並創建新controller的監聽。

組件移除
組件移除,例如頁面銷毀的時候會依次執行:deactivate > dispose

deactivate
Called when this object is removed from the tree.

在dispose之前,會調用這個函數。實測在組件可見狀態變化的時候會調用,當組件卸載時也會先一步dispose調用。

dispose
Called when this object is removed from the tree permanently.

調用次數:1次

一旦到這個階段,組件就要被銷毀了,這個函數一般會移除監聽,清理環境。

##reassemble
hot reload調用

名稱狀態
initState 插入渲染樹時調用,只調用一次
didChangeDependencies state依賴的對象發生變化時調用
didUpdateWidget 組件狀態改變時候調用,可能會調用多次
build 構建Widget時調用
deactivate 當移除渲染樹的時候調用
dispose 組件即將銷毀時調用


實際場景
假設我們從A頁面跳轉到B頁面, 那麽A,B頁面的生命周期會是怎樣的呢?

B頁面進入初始化狀態,依次執行4個函數:構造函數 > initState > didChangeDependencies > Widget build , 此時頁面加載完成,進入運行態。
此時A頁面依次執行deactivate > build函數。註意 此時A頁面並未卸載。

然後我們假設B頁面只有一個按鈕,點擊B頁面中的按鈕,改變按鈕的文字,會執行widget的build方法 ,(理論上也應該執行didUpdateWidget,但我這裏沒有)。

這時,我們點擊返回鍵從B頁面返回到A頁面。
A頁面重新顯示,B頁面開始卸載。
那麽A先執行deactivate > build , 然後B頁面依次執行:deactivate > dispose 。
此時A頁面進入運行態,B頁面移除。

本次示例B頁面代碼:

import ‘package:flutter/material.dart‘;

class NewsDetailPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => NewsDetailState();
}

class NewsDetailState extends State<NewsDetailPage> {
  int text = 1;

  NewsDetailState() {
    print(‘構造函數‘);
  }

  @override
  void initState() {
    print(‘init state‘);
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print(‘didChangeDependencies‘);
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    print(‘widget build‘);

    return Scaffold(
      body: Center(
        child: _loading(),
      ),
      appBar: AppBar(
        title: Text(‘咨詢詳情‘),
      ),
    );
  }

  @override
  void didUpdateWidget(NewsDetailPage oldWidget) {
    print(‘組件狀態改變:didUpdateWidget‘);
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    print(‘移除時:deactivate‘);
    super.deactivate();
  }

  @override
  void dispose() {
    print(‘移除時:dispose‘);
    super.dispose();
  }

  //預加載布局
  Widget _loading() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        CircularProgressIndicator(
          strokeWidth: 1.0,
        ),
        Container(
          child: Text("正在加載"),
          margin: EdgeInsets.only(top: 10.0),
        )
      ],
    );
  }
}

  

Tips:
下面內容來自鹹魚技術團隊.

當ListView中的item滾動出可顯示區域的時候,item會被從樹中remove掉,此item子樹中所有的state都會被dispose,state記錄的數據都會銷毀,item滾動回可顯示區域時,會重新創建全新的state、element、renderobject

使用hot reload功能時,要特別註意state實例是沒有重新創建的,如果該state中存在一下復雜的資源更新需要重新加載才能生效,那麽需要在reassemble()添加處理,不然當你使用hot reload時候可能會出現一些意想不到的結果,例如,要將顯示本地文件的內容到屏幕上,當你開發過程中,替換了文件中的內容,但是hot reload沒有觸發重新讀取文件內容,頁面顯示還是原來的舊內容.

didChangeDependencies有兩種情況會被調用。

創建時候在initState 之後被調用

在依賴的InheritedWidget發生變化的時候會被調用

正常的退出流程中會執行deactivate然後執行dispose。但是也會出現deactivate以後不執行dispose,直接加入樹中的另一個節點的情況。

這裏的狀態改變包括兩種可能:1.通過setState內容改變 2.父節點的state狀態改變,導致孩子節點的同步變化。

App生命周期
需要指出的是如果想要知道App的生命周期,那麽需要通過WidgetsBindingObserver的didChangeAppLifecycleState 來獲取。通過該接口可以獲取是生命周期在AppLifecycleState類中。常用狀態包含如下幾個:

名稱狀態
resumed 可見並能響應用戶的輸入
inactive 處在並不活動狀態,無法處理用戶響應
paused 不可見並不能響應用戶的輸入,但是在後臺繼續活動中


一個實際場景中的例子:

在不考慮suspending的情況下:從後臺切入前臺生命周期變化如下: AppLifecycleState.inactive->AppLifecycleState.resumed;

從前臺壓後臺生命周期變化如下: AppLifecycleState.inactive->AppLifecycleState.paused;


原文:https://blog.csdn.net/u011272795/article/details/82695920

flutter中的生命周期函數