flutter中的生命週期函數

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

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


大體能夠分爲3個階段:android

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

而後咱們看一下每一個函數的意義:框架

構造函數
調用次數:1次ide

這個函數嚴格意義上來說不屬於生命週期的一部分,由於這個時候State的widget屬性爲空,沒法在構造函數中訪問widget的屬性 。可是構造函數必然是要第一個調用的。能夠在這一部分接收前一個頁面傳遞過來的數據。函數

initState
Called when this object is inserted into the tree.佈局

調用次數:1次ui

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

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

相關文章
相關標籤/搜索