Flutter - Widget-Context-Stage

讀前須知:此篇文章基本上是Widget - State - Context - InheritedWidget的翻譯而且刪減了部分我我的以爲沒有意義的文字,保留下來的部分也不會逐字逐句精確翻譯,因此其實強烈推薦閱讀英文原文。app

如下文章裏面除了第一張思惟導圖是本人所做以外,凡是出現的示例代碼和圖片都是上面提到的英文文章裏面的。本人在這裏對此表示感激和愧疚,若是涉及到侵權,本人會當即刪掉整篇文章。less

All the example codes and images used in this article are from Widget - State - Context - InheritedWidget excepting the first one. If this is of tort, I will delete this article immediately.ide

正文開始:函數

Widget, State 和Context是每個flutter開發者必需要徹底理解的概念,可是具體來講要理解哪些知識呢?這篇文章會就如下幾個知識點進行講解:佈局

1: Stateful和Stateless widget的差異動畫

2:什麼是Contextui

3:  什麼是State以及怎麼使用this

4:一個context和他的state之間的關係spa

5:InheritedWidget以及在在Widget樹裏面怎麼傳遞信息翻譯

6:rebuild的概念

接下來文章會按照如下結構去展開:

image

PS:因爲此篇的篇幅很長,此思惟導圖裏面的第二部分(左邊的’怎樣獲取State‘)的篇幅也會很長,因此第二部分會放到下一篇文章講解。

一:基本概念解釋

1:什麼是Widget

Widget即組件。在flutter裏面幾乎萬物都是Widget,跟咱們常說的component是同一個概念。

2: 什麼是Widget tree

Widget按照樹形結構組合的產物就是Widget tree。這個Widget tree的每一個節點上又是一個Widget。包含其餘組件的組件叫作父組件,被其餘組件包含的組件叫作子組件。好比下面一段代碼:

@override
Widget build(BuildContext){
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
}

上面的一段代碼,若是咱們用圖像表示它的widget tree的話就以下圖所示:
圖片描述

3:什麼是Context
一個context標識了一個widget在widget tree的結構中是在哪一個地方被build的。簡而言之就是一個context標明瞭一個widget是掛載在widget tree的那個節點的。

一個context只屬於一個widget。
假如一個widget A有子組件,那麼widget A的context會變成子組件的父context。

以上文提到的widget tree爲例子,假如咱們畫出它的context,以下圖所示(一個顏色表明一個context):
圖片描述

Context Visibility(上下文可見性)

只在其本身的context或者在其父context可見。
從以上描述,咱們能夠輕易地找到一個widget的父widget,例如:

Scaffold > Center > Column > Text:
context.ancestorWidgetOfExtractType(Scaffold) => 會找到第一個沿着Text的context一路向上而遇到的第一個Scaffold.

從一個父組件,也能找到子組件,可是不建議這麼作(稍後會討論到)。

4:Widget的分類
在Flutter裏面有2類widget:

  • Stateless Widget
  • Stateful Widget

從字面意思上能夠理解Stateless Widget就是無狀態的組件,Stateful Widget是有狀態的組件,接下來咱們對兩者作更具體的講解。
Stateless Widget
有些組件只依賴於他們本身的配置信息,這些配置信息通常是由他們的父組件在build他們的時候提供。
換句話說,這些組件一旦建立以後,就不用擔憂任何變化。
這類型的組件就是Stateless Widget.
典型的例子好比Text, Row, Column, Container等,對於這些組件來講在build的時候,咱們只須要傳一些參數給他們就好。
一個Stateless Widget只能被build一次,意思就是一旦build,不會由於任何的事件或者用戶行爲而從新build。

Stateless Widget lifecycle
下面是一個典型的Stateless Widget的代碼結構。
如咱們所見,咱們傳遞一些闡述給它的構造函數,可是請記住,這些參數不會再以後被改變了,因此通常你也會看到這些參數是被定義爲final的。

class MyStatelessWidget extends StatelessWidget {

    MyStatelessWidget({
        Key key,
        this.parameter,
    }): super(key:key);

    final parameter;

    @override
    Widget build(BuildContext context){
        return new ...
    }
}

雖然有另外一個方法(createElement)能夠被overridden,可是通常沒人會用到它。惟一一個須要被override的方法就是build().
Stateless widget的生命週期是直接而簡單的,以下所示:

  • 初始化
  • 經過build()方法渲染

Stateful Widget

一些其餘的組件所擁有的數據會在組件生命週期內產生變化,這些數據就變成了dynamic(動態的)的。

這些被組件擁有的會在組件的生命週期內改變的數據的列表,咱們叫作State

而擁有以上特色的組件,咱們就叫作Stateful Widget

Stateful Widget的例子就比如一個用戶能夠選擇的Checkboxes的列表或者一個會根據某種條件而disabled的Button。

5: 什麼是State
一個State定義了一個StatefulWidget的「行爲」部分。
State包含了如下旨在與一個組件交互的:

  • 行爲(behaviour)
  • UI佈局(layout)

任何對State的改變都會致使這個組件的rebuild。

6:State和Context之間的關係
對Stateful Widgets而言,一個State是和一個Context是綁定的,並且這種綁定關係是永久的,一個State永遠不會改變他的Context。

即便一個組件的Context在Widget tree上發生了移動,這個State仍是會依然和那個Context綁定在一塊兒。

很是重要的知識點:
由於一個State對象和一個Context是綁定的,這就意味着這個State對象不能從其餘的Context下直接被獲取到(這一點以後會討論到)

7:StatefulWidget的生命週期(lifecycle)
前面已經介紹和不少StatefulWidget的相關概念和基礎知識,接下來咱們來了解一下StatefulWidget的生命週期,這裏不會介紹所有的生命週期,先只挑幾個重要的,與本篇文章的主旨相關的幾個來說。先看下下面一個StatefulWidget的例子:

class MyStatefulWidget extends StatefulWidget {

    MyStatefulWidget({
        Key key,
        this.parameter,
    }): super(key: key);
    
    final parameter;
    
    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

    @override
    void initState(){
        super.initState();
        
        // Additional initialization of the State
    }
    
    @override
    void didChangeDependencies(){
        super.didChangeDependencies();
        
        // Additional code
    }
    
    @override
    void dispose(){
        // Additional disposal code
        
        super.dispose();
    }
    
    @override
    Widget build(BuildContext context){
        return new ...
    }
}

下面的這個圖展現了(一個簡化的版本)一個StatefuleWidget在建立的時候,內部的一些列行爲。在這個圖的右邊你能夠看到一個State對象的內部狀態變化以及最右邊的Context是在何時和State產出聯繫而所以變爲可用的。
圖片描述

initState()
initState()是在構造函數以後的第一個被調用的生命週期方法。當你須要再初始化階段作一個額外的操做的時候,你須要override它。典型的在initState()方法裏額外的操做好比動畫,或者某些數據準備。假如你override initState(),記得調用super.initState()而且這行代碼要放在initState()方法體的最前面。意思就是你得讓super.initState()執行了以後再去執行你額外的初始化工做。

在這個階段,一個context是存在的,可是你並不能真正地使用它,由於這時候context和state尚未完成綁定。

一旦initState()執行完畢,State對象就初始化好了,context也就能夠被使用了。

initState()在整個生命週期內只會被調用一次

didChangeDependencies()
didChangeDependencies()是在生命週期裏第二個被調用的方法。

這個階段,context已經能夠被使用了。

假如你的組件是連接到了InheritedWidget,根據context你須要初始化一些listeners(監聽者),一般你須要override這個方法。

注意,若是某個組件是連接了InheritedWidget,那麼這個組件每次重建(rebuild),didChangeDependencies()都會被調用。

假如你要override這個方法,你應該首先調用super.didChangeDependencies().

build()
build()跟在didChangeDependencies()(和didUpdateWidget())以後被調用。

這個方法就是用來構建你的組件的。

每一次State對象發生變化(或者InheritedWidget須要通知它的註冊者)時,build()方法都會被調用。

一般,咱們經過調用setState((){…})來改變State對象,強制build()被調用,從而從新build咱們的widget。

dispose()
dispose()在這個組件被銷燬的時候被調用。

通常咱們override這個方法,能夠在組件被銷燬的時候作一個清理工做。

override dispose()記得調用super.dispose()而且把它放在方法體的最後

8: StatelessWidget和StatefulWidget之間的抉擇
既然在Flutter裏面有StatelessWidget和StatefulWidget這兩種類型的組件,那在這兩者之間如何抉擇呢?

記住標準就是:
在這個組件的生命週期內,是否有會變化的數據,這個組件是否須要rebuild?

若是答案是yes,那你就須要一個StatefulWidget而不會StatelessWidget。

9:StatefulWidget的2個組成部分

組件的構造函數部分

class MyStatefulWidget extends StatefulWidget {
    MyStatefulWidget({
        Key key,
        this.color,
    }): super(key: key);
    
    final Color color;

    @override
    _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

這部分是一個StatefulWidget的public部分。這部分不會在一個組件的生命週期內發生改變,它只是接收一些參數以便它的State可使用。好比上面這個例子裏面的color這個參數。

Widget State定義部分

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
    ...
    @override
    Widget build(BuildContext context){
        ...
    }
}

_MyStatefulWidgetState是這個Widget在其生命週期內變化的部分,也是使得這個Widget可以rebuild的部分。
_MyStatefulWidgetState經過widget.{name of the variable}能夠獲取存在MyStatefulWidget內的任意變量。例如這裏,能夠經過widget.color獲取color變量。

10:Widget的惟一標識-key
在Flutter裏面,每個組件都是惟一標識的,這個惟一標識是在build的時候被定義的。

這個惟一的標識就是組件的可選參數:Key. 加入key缺省了,系統會默認給你建立一個。

在某些情形下,你必須強制制定key,以便你能夠經過這個key獲取到這個組件。

你能夠經過下面的一些helper來達到上面的目的:GlobalKey, LocalKey, UniqueKey或者ObjectKey。

GlobalKey保證這個key在整個application裏面都是惟一的。

如下的例子就是保證myKey在整個application都是惟一的:

GlobalKey myKey = new GlobalKey();
    ...
    @override
    Widget build(BuildContext context){
        return new MyWidget(
            key: myKey
        );
    }
PS:因爲此篇的篇幅已經夠長,以前的思惟導圖裏面的第二部分的篇幅也會很長,因此第二部分會放到下一篇文章講解。
相關文章
相關標籤/搜索