讀前須知:此篇文章基本上是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的概念
接下來文章會按照如下結構去展開:
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
有些組件只依賴於他們本身的配置信息,這些配置信息通常是由他們的父組件在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的生命週期是直接而簡單的,以下所示:
Stateful Widget
一些其餘的組件所擁有的數據會在組件生命週期內產生變化,這些數據就變成了dynamic(動態的)的。
這些被組件擁有的會在組件的生命週期內改變的數據的列表,咱們叫作State。
而擁有以上特色的組件,咱們就叫作Stateful Widget。
Stateful Widget的例子就比如一個用戶能夠選擇的Checkboxes的列表或者一個會根據某種條件而disabled的Button。
5: 什麼是State
一個State定義了一個StatefulWidget的「行爲」部分。
State包含了如下旨在與一個組件交互的:
任何對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:因爲此篇的篇幅已經夠長,以前的思惟導圖裏面的第二部分的篇幅也會很長,因此第二部分會放到下一篇文章講解。