iOSer 的 Flutter 快速入坑之道(二)

iOSer 的 Flutter 快速入坑之道(二)

前言

本文是繼上一篇的有關(guān) flutter 的第二篇文章摆尝,由于合起來篇幅太長良姆,所以考慮分開成多篇文章上傳啦蚁堤。

Flutter UI

a. Widget

在 iOS 中嫩实,大部分控件都是基于 UIView 的實例刽辙,所有的控件實例在添加到視圖中后,會被作為節(jié)點添加到 UI 渲染樹中舶赔。其實在 Flutter 中的 Widget 概念也是類似扫倡,都會由一棵渲染樹來維護所有的 Widget 子節(jié)點。

但是不同之處在于竟纳,第一撵溃,F(xiàn)lutter 中所有的 widget 在它的生命周期中都是不可變的。在 iOS 中锥累,假如我們要修改并重新渲染一個控件缘挑,可以調(diào)用 setNeedDisplay 方法,然后在這個控件的節(jié)點開始桶略,重新跑一邊所有子節(jié)點语淘,渲染生成新的視圖,但是在這個過程中际歼,控件只是產(chǎn)生的屬性的修改惶翻,實例卻還是那個實例。然而在 Flutter 中就不同了鹅心,由于 Flutter 中的 Widget 都是不可變的吕粗,所以在當(dāng)某個 Widget 需要修改的時候,F(xiàn)lutter 會創(chuàng)建一個新的 Widget 來做替換旭愧。

第二颅筋,其實在 Flutter 中,Widget 不同于 View 的另一個區(qū)別就是 Widget 并不負(fù)責(zé)任何渲染相關(guān)的事情输枯。那么是誰在負(fù)責(zé)渲染呢议泵?Flutter 是怎么樣通過 Widget 來渲染出界面的?在這里我們就要了解 Flutter 中的 Widget桃熄、Element先口、RenderObject 之間的聯(lián)系啦。

Widget 中包含屬性 Element,Element 中又包含了 RenderObject 屬性碉京。Widget 就是一個對 Element 的配置或者描述桩引,我們開發(fā)者也是只需要和 Widget 打交道,并不需要去維護更新渲染樹收夸。

Element 就是負(fù)責(zé)維護渲染樹的實例坑匠,主要作用在 Flutter 的渲染管線 build 階段,可以把 element 看作真正的渲染樹子節(jié)點卧惜。

RenderObject 是用于渲染的對象厘灼,主要作用在 Flutter 的渲染管線 布局以及繪制 階段,也就是它將我們的界面最終繪制在屏幕上咽瓷。

關(guān)于 Flutter 的底層渲染機制设凹,其實我認(rèn)為作為每個初學(xué) Flutter 的同學(xué)都需要了解,只有更深入的了解它的渲染機制茅姜,才能更好地去使用它闪朱。

深入了解Flutter界面開發(fā)

b.如何使用 Widget 構(gòu)建界面?

在上面我們大概了解了 Widget 是什么東西钻洒,以及 flutter 的渲染機制奋姿,那么實際開發(fā)中如何使用 Widget呢?

還是從 hello world 開始吧素标!

import 'package:flutter/material.dart';
 void main() => runApp(new MyApp());

 class MyApp extends StatelessWidget {
 @override
  Widget build(BuildContext context) {
      return new MaterialApp(
         title: 'Welcome to Flutter',
             home: new Scaffold(
                 appBar: new AppBar(
                  title: new Text('Welcome to Flutter'),
                  ),
          body: new Center(
               child: new Text('Hello World'),
           ),
          ),
       );
      }
 }

所有的 Widget 都是通過這種嵌套的方式來表示其相互之間的關(guān)系以及配置称诗,其實很容易懂,不懂的話多看幾種 Widget 的例子就知道啦头遭。
Flutter 中文網(wǎng)

c.StatelessWidget 和 StatefulWidget

StatelessWidget 是不存在中間狀態(tài)的控件寓免,也就是說當(dāng)它被 new 出來之后,就不會被改變了计维,也無法被改變袜香。如果需要更新展示的內(nèi)容,就只能銷毀掉重新 new 一個鲫惶。

StatefulWidget 是可以保存中間狀態(tài)的控件蜈首,控件的中間狀態(tài)保存在 State 類中,通過調(diào)用 setState(){} 方法來觸發(fā)更新機制剑按,將此節(jié)點將其以下的整個子樹重新更新繪制疾就。

d.setState 機制介紹

setState 的作用是告訴框架這個 Object 的 state 已經(jīng)被修改了澜术,那么這個修改它可能會對 UI 有影響艺蝴,所以需要重新 build 一遍該 Object。

首先我們來了解一下一個 [State]Object 的生命周期狀態(tài)鸟废。

 enum _StateLifeCycle {
 created,
 initialized,
 ready,
 defunct,
 }

當(dāng)這個[State]Object 剛被創(chuàng)建時猜敢,它的狀態(tài)就是 created 的狀態(tài)。緊接著 Object 會調(diào)用 didChangeDependencies 方法,這時候 Object 的狀態(tài)會轉(zhuǎn)變?yōu)?initialized缩擂,但是此時鼠冕,object 并沒有做好被 build 的準(zhǔn)備。然后接下來胯盯,object 會轉(zhuǎn)變?yōu)?ready 的狀態(tài)懈费,表示現(xiàn)在已經(jīng)做好 build 的準(zhǔn)備了。object 會保持這個狀態(tài)博脑,直到 dispose 方法被調(diào)用憎乙。一旦 object 調(diào)用了 dispose 方法,那么它的狀態(tài)就會轉(zhuǎn)變成 defunct 的狀態(tài)叉趣。

然后我們可以看到 setState 的源碼泞边。首先 flutter 會判斷 setState 是否在 Object 調(diào)用 dispose 銷毀后再調(diào)用,若 state 為 defunct 疗杉,說明該 Object 已經(jīng)被銷毀了阵谚,報錯。

  if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
      throw error;
      }

接下來我們看到 flutter 還判斷了 object 的狀態(tài)是否為 created 以及 是否非 mounted烟具。那么 mounted 屬性是什么呢梢什?

mounted 屬性代表的意思是這個 object 是否有被 buildContext 所持有。我們知道 mounted 有安裝朝聋、加入的意思绳矩。在一個[state]Object 調(diào)用 initState 方法之前,flutter 會把該[state]Object 通過與 buildContext 關(guān)聯(lián)的方式嵌入玖翅,直至 Object 調(diào)用了 dispose翼馆,也就是不會再調(diào)用 build 了,這個 Object 才不被繼續(xù)持有金度。

所以這兩個條件說明該 [state]Object 還未被加入到 widget 樹中应媚,有可能是在構(gòu)造函數(shù)的時候調(diào)用了 setState,這個是沒有必要的猜极,因為 object 剛被創(chuàng)建的時候是被標(biāo)記為 dirty 的中姜,需要調(diào)用 build。

    final dynamic result = fn() as dynamic;

接下來是判斷如果 state 中的更新函數(shù)為一個異步函數(shù)跟伏,返回值是 Future 類型的丢胚,那么也報錯。

最后受扳,才是調(diào)用了 _element.markNeedsBuild 方法携龟。那么 markNeedsBuild 函數(shù)做了什么事情呢。首先勘高,他會判斷 state 是否為 defunct,elementLifeStyle 是否為 active 以及 owner 是否為 nil峡蟋。之后坟桅,將這些節(jié)點都標(biāo)記為 dirty,然后加入到全局的 widget 隊列中蕊蝗,在下一幀的時候進行重建仅乓。

那么什么是 owner,owner 的作用是什么呢蓬戚?

我們可以看到 owner 其實是一個 BuildOwner 的實例夸楣,它主要負(fù)責(zé)跟蹤需要 rebuild 的 widgets。 然后在 scheduleBuildFor 方法中子漩,首先是判斷 element 的 _inDirtyList 是否為 true裕偿,如果是,就說明它已經(jīng)在 dirtyList 中了痛单,于是把它的 dirtyElementsNeedsResorting 屬性設(shè)置為yes嘿棘,返回。
如果一切正常旭绒,那么接下來會調(diào)用到 onBuildScheduled()方法鸟妙,這是一個方法回調(diào),是 VoidCallback 類型挥吵。

那么接著重父,onBuildScheduled 的回調(diào)是在哪里呢。通過搜索我們發(fā)現(xiàn)在 WigetsBinding 的 initInstances 方法中有賦值 onBuildScheduled 代碼塊忽匈。

WigetsBinding 又是什么房午?! 從文檔中我們可以了解到丹允,文檔說 WidgetsBinding 是 wigetsLayer 和 flutter 引擎的粘合劑郭厌。。

好的雕蔽,不管他折柠,先繼續(xù)看內(nèi)部實現(xiàn),我們繼續(xù)往下點追蹤到一個叫
ensureVisualUpdate 的函數(shù)批狐,在其中的一個 case 中扇售,我們看到了 scheduleFrame 的方法。然后再往這個方法里面看嚣艇,可以看到最終它調(diào)用了 window.scheduleFrame 方法承冰。 window 的 scheduleFrame 方法會生成一個新的幀,在這個方法調(diào)用之后食零,flutter 引擎會最終調(diào)用 handleBeginFrame 強制刷新一個新幀困乒,無論之前那一幀是否已經(jīng)渲染結(jié)束了。

所以到這里差不多就是 setState 調(diào)用到屏幕渲染刷新的大致過程了慌洪。整理一下整個過程中調(diào)用的函數(shù)

State.setState() -> 
Element.markNeedsBuild() -> 
BuildOwner.scheduleBuildFor() -> 
WidgetBinding._handleBuildScheduled() -> 
SchedulerBinding.ensureVisualUpdate() -> 
SchedulerBinding.scheduleFrame() -> 
Window.scheduleFrame() -> 
WidgetBinding.drawFrame()    

e.如何做控件的顯示和隱藏顶燕?

我們已經(jīng)了解了 flutter 中 setState 的機制以及 widget 的一些特性了,那么實際開發(fā)中碰到對 widget 進行顯示隱藏控制等需求的時候冈爹,應(yīng)該怎么處理呢涌攻?

大概的簡單思路就是通過一個變量 t 去表示 widget 的顯示和隱藏,然后再封裝一個函數(shù)根據(jù)這個變量來返回需要顯示的 widget 或 Container()频伤。在 flutter 中恳谎,如果需要表示返回空的一個 widget 的時候,不要返回 null憋肖,而是返回 Container()因痛。最后在需要修改變量 t 的時候加上 setState 代碼塊,表示需要重新繪制岸更。

class _TestState extends State<Test> {
  bool toggle = true;
  void _toggle() {
    setState(() {
      toggle = !toggle;
    });
  }

  _getToggleChild() {
    if (toggle) {
      return Text('Text');
    } else {
      return Container();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Test App"),
      ),
      body: Center(
        child: _getToggleChild(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        child: Icon(Icons.update),
      ),
    );
  }
}   

以上是通過一個按鈕點擊控制控件顯示與否的簡單代碼鸵膏,蠻看下~

最后

這邊主要講有關(guān) flutter UI 方面的東西,當(dāng)然 flutter UI 相關(guān)的東西也遠(yuǎn)遠(yuǎn)不止這些怎炊,所以如果后續(xù)有補充的地方谭企,我也會在本文中修改跟進~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市评肆,隨后出現(xiàn)的幾起案子债查,更是在濱河造成了極大的恐慌,老刑警劉巖瓜挽,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盹廷,死亡現(xiàn)場離奇詭異,居然都是意外死亡久橙,警方通過查閱死者的電腦和手機俄占,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淆衷,“玉大人颠放,你說我怎么就攤上這事】愿遥” “怎么了碰凶?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鹿驼。 經(jīng)常有香客問我欲低,道長,這世上最難降的妖魔是什么畜晰? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任砾莱,我火速辦了婚禮,結(jié)果婚禮上凄鼻,老公的妹妹穿的比我還像新娘腊瑟。我一直安慰自己聚假,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布闰非。 她就那樣靜靜地躺著膘格,像睡著了一般。 火紅的嫁衣襯著肌膚如雪财松。 梳的紋絲不亂的頭發(fā)上瘪贱,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音辆毡,去河邊找鬼菜秦。 笑死,一個胖子當(dāng)著我的面吹牛舶掖,可吹牛的內(nèi)容都是我干的球昨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼眨攘,長吁一口氣:“原來是場噩夢啊……” “哼褪尝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起期犬,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤河哑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后龟虎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體璃谨,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年鲤妥,在試婚紗的時候發(fā)現(xiàn)自己被綠了佳吞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡棉安,死狀恐怖底扳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贡耽,我是刑警寧澤衷模,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站蒲赂,受9級特大地震影響阱冶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜滥嘴,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一木蹬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧若皱,春花似錦镊叁、人聲如沸尘颓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疤苹。三九已至蛔添,卻和暖如春痰催,著一層夾襖步出監(jiān)牢的瞬間兜辞,已是汗流浹背迎瞧。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逸吵,地道東北人凶硅。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像扫皱,于是被迫代替她去往敵國和親足绅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 原文在此韩脑,此處只為學(xué)習(xí) Widget與ElementWidget主要接口Stateless WidgetState...
    lltree閱讀 4,501評論 0 1
  • 作為家長您是怎么對待孩子玩手機的問題的,強制沒收對孩子來說已經(jīng)不起作用了进苍,反而易造成孩子的逆反心理加缘,今天這篇文章給...
    子曰語果閱讀 482評論 0 0
  • (十二) 許白曼在晉國府上住了些許日子,她能感覺到唐予正對她的感情觉啊,只是大仇未報拣宏,自己戴罪之身也未沉冤昭雪,哪還顧...
    老死不死閱讀 856評論 0 54
  • 走不動的老人杠人,搬不走的墓碑 就讓他們留守在時光里 守候流水般空寂的鄉(xiāng)村 日暮降至 興致而歸的羊群勋乾,攆著薄薄的黃昏 ...
    莫名小情許閱讀 130評論 0 0
  • 感賞自己每天堅持學(xué)習(xí)讀書,每天進步那么一點點都值得高興嗡善。 大女兒看手機時間太長市俊,昨晚她爸爸要準(zhǔn)備說她被我制止了,感...
    延愛閱讀 152評論 0 3