国产亚洲欧美人成在线,免费视频爱爱太爽了无码,日本免费一区二区三区高清视频 ,国产真实伦对白精彩视频

歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
電話圖標(biāo) 余先生:13699882642

網(wǎng)站百科

為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴

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

發(fā)表日期:2018-07 文章編輯:小燈 瀏覽次數(shù):2377

作者:閑魚技術(shù)-朝空

概要

本文不是flutter界面開發(fā)入門文章,而是一篇深入介紹Flutter framework關(guān)于視圖樹的創(chuàng)建與管理機(jī)制、布局、渲染的原理、以及flutter布局與渲染相關(guān)性能優(yōu)化的設(shè)計(jì)思路的文章。同時(shí)介紹在使用flutter開發(fā)過程中,遇到的一些坑和相應(yīng)的解決方案。

Flutter框架簡(jiǎn)介

image
  1. 跨平臺(tái)應(yīng)用的框架,沒有使用WebView或者系統(tǒng)平臺(tái)自帶的控件,使用自身的高性能渲染引擎(Skia)自繪,
  2. 界面開發(fā)語言使用dart,底層渲染引擎使用C, C++
  3. 組合大于繼承,控件本身通常由許多小型、單用途的控件組成,結(jié)合起來產(chǎn)生強(qiáng)大的效果,類的層次結(jié)構(gòu)是扁平的,以最大化可能的組合數(shù)量

Rendering Pipeline

image

本文主要介紹build、layout、paint的三個(gè)階段

視圖樹

Widget&Element&RenderObject

image

flutter視圖樹包含了三種樹,上圖只是介紹了三顆樹的基礎(chǔ)class的對(duì)應(yīng)關(guān)系和功能介紹

創(chuàng)建樹

  1. 創(chuàng)建widget樹
  2. 調(diào)用runApp(rootWidget),將rootWidget傳給rootElement,做為rootElement的子節(jié)點(diǎn),生成Element樹,由Element樹生成Render樹


    image
  • Widget:存放渲染內(nèi)容、視圖布局信息,widget的屬性最好都是immutable(如何更新數(shù)據(jù)呢?查看后續(xù)內(nèi)容)
  • Element:存放上下文,通過Element遍歷視圖樹,Element同時(shí)持有Widget和RenderObject
  • RenderObject:根據(jù)Widget的布局屬性進(jìn)行l(wèi)ayout,paint Widget傳人的內(nèi)容

更新樹

為什么widget都是immutable?

flutter界面開發(fā)是一種響應(yīng)式編程,主張simple is fast,flutter設(shè)計(jì)的初衷希望數(shù)據(jù)變更時(shí)發(fā)送通知到對(duì)應(yīng)的可變更節(jié)點(diǎn)(可能是一個(gè)StatefullWidget子節(jié)點(diǎn),也可以是rootWidget),由上到下重新create widget樹進(jìn)行刷新,這種思路比較簡(jiǎn)單,不用關(guān)心數(shù)據(jù)變更會(huì)影響到哪些節(jié)點(diǎn)。

widget重新創(chuàng)建,element樹和renderObject樹是否也重新創(chuàng)建?

widget只是一個(gè)配置數(shù)據(jù)結(jié)構(gòu),創(chuàng)建是非常輕量的,加上flutter團(tuán)隊(duì)對(duì)widget的創(chuàng)建/銷毀做了優(yōu)化,不用擔(dān)心整個(gè)widget樹重新創(chuàng)建所帶來的性能問題,但是renderobject就不一樣了,renderobject涉及到layout、paint等復(fù)雜操作,是一個(gè)真正渲染的view,整個(gè)view 樹重新創(chuàng)建開銷就比較大,所以答案是否定的。

樹的更新規(guī)則

  1. 找到widget對(duì)應(yīng)的element節(jié)點(diǎn),設(shè)置element為dirty,觸發(fā)drawframe, drawframe會(huì)調(diào)用element的performRebuild()進(jìn)行樹重建
  2. widget.build() == null, deactive element.child,刪除子樹,流程結(jié)束
  3. element.child.widget == NULL, mount 的新子樹,流程結(jié)束
  4. element.child.widget == widget.build() 無需重建,否則進(jìn)入流程5
  5. Widget.canUpdate(element.child.widget, newWidget) == true,更新child的slot,element.child.update(newWidget)(如果child還有子節(jié)點(diǎn),則遞歸上面的流程進(jìn)行子樹更新),流程結(jié)束,否則轉(zhuǎn)6
  6. Widget.canUpdate(element.child.widget, newWidget) != true(widget的classtype 或者 key 不相等),deactivew element.child,mount 新子樹

注意事項(xiàng):

  1. element.child.widget == widget.build(),不會(huì)觸發(fā)子樹的update,當(dāng)觸發(fā)update的時(shí)候,如果沒有生效,要注意widget是否使用舊widget,沒有new widget,導(dǎo)致update流程走到該widget就停止了
  2. 子樹的深度變化,會(huì)引起子樹重建,如果子樹是一個(gè)復(fù)雜度很高的樹,可以使用GlobalKey做為子樹widget的key。GlobalKey具有緩存功能

如何觸發(fā)樹更新

  1. 全局更新:調(diào)用runApp(rootWidget),一般flutter啟動(dòng)時(shí)調(diào)用后不再會(huì)調(diào)用
  2. 局部子樹更新, 將該子樹做StatefullWidget的一個(gè)子widget,并創(chuàng)建對(duì)應(yīng)的State類實(shí)例,通過調(diào)用state.setState() 觸發(fā)該子樹的刷新

Widget

StatefullWidget vs StatelessWidget

  1. StatelessWidget:無中間狀態(tài)變化的widget,需要更新展示內(nèi)容就得通過重新new,flutter推薦盡量使用StatelessWidget
  2. StatefullWidget:存在中間狀態(tài)變化,那么問題來了,widget不是都immutable的,狀態(tài)變化存儲(chǔ)在哪里?flutter 引入state的類用于存放中間態(tài),通過調(diào)用state.setState()進(jìn)行此節(jié)點(diǎn)及以下的整個(gè)子樹更新

State 生命周期

  1. initState(): state create之后被insert到tree時(shí)調(diào)用的
  2. didUpdateWidget(newWidget):祖先節(jié)點(diǎn)rebuild widget時(shí)調(diào)用
  3. deactivate():widget被remove的時(shí)候調(diào)用,一個(gè)widget從tree中remove掉,可以在dispose接口被調(diào)用前,重新instert到一個(gè)新tree中
  4. didChangeDependencies():
    • 初始化時(shí),在initState()之后立刻調(diào)用
    • 當(dāng)依賴的InheritedWidget rebuild,會(huì)觸發(fā)此接口被調(diào)用
  5. build():
    • After calling [initState].
    • After calling [didUpdateWidget].
    • After receiving a call to [setState].
    • After a dependency of this [State] object changes (e.g., an[InheritedWidget] referenced by the previous [build] changes).
    • After calling [deactivate] and then reinserting the [State] object into the tree at another location.
  6. dispose():Widget徹底銷毀時(shí)調(diào)用
  7. reassemble(): hot reload調(diào)用

注意事項(xiàng):

  1. A頁面push一個(gè)新的頁面B,A頁面的widget樹中的所有state會(huì)依次調(diào)用deactivate(), didUpdateWidget(newWidget)、build()(這里懷疑是bug,A頁面push一個(gè)新頁面,理論上并沒有將A頁面進(jìn)行remove操作),當(dāng)然從功能上,沒有看出來有什么異常
  2. 當(dāng)ListView中的item滾動(dòng)出可顯示區(qū)域的時(shí)候,item會(huì)被從樹中remove掉,此item子樹中所有的state都會(huì)被dispose,state記錄的數(shù)據(jù)都會(huì)銷毀,item滾動(dòng)回可顯示區(qū)域時(shí),會(huì)重新創(chuàng)建全新的state、element、renderobject
  3. 使用hot reload功能時(shí),要特別注意state實(shí)例是沒有重新創(chuàng)建的,如果該state中存在一下復(fù)雜的資源更新需要重新加載才能生效,那么需要在reassemble()添加處理,不然當(dāng)你使用hot reload時(shí)候可能會(huì)出現(xiàn)一些意想不到的結(jié)果,例如,要將顯示本地文件的內(nèi)容到屏幕上,當(dāng)你開發(fā)過程中,替換了文件中的內(nèi)容,但是hot reload沒有觸發(fā)重新讀取文件內(nèi)容,頁面顯示還是原來的舊內(nèi)容

數(shù)據(jù)流轉(zhuǎn)

從上往下

數(shù)據(jù)從根往下傳數(shù)據(jù),常規(guī)做法是一層層往下,當(dāng)深度變大,數(shù)據(jù)的傳輸變的困難,flutter提供InheritedWidget用于子節(jié)點(diǎn)向祖先節(jié)點(diǎn)獲取數(shù)據(jù)的機(jī)制,如下例子:

class FrogColor extends InheritedWidget {const FrogColor({Key key,@required this.color,@required Widget child,}) : assert(color != null), assert(child != null), super(key: key, child: child); final Color color; static FrogColor of(BuildContext context) {return context.inheritFromWidgetOfExactType(FrogColor);} @overridebool updateShouldNotify(FrogColor old) => color != old.color; } 

child及其以下的節(jié)點(diǎn)可以通過調(diào)用下面的接口讀取color數(shù)據(jù)
FrogColor.of(context).color

說明:BuildContext 就是Element的一個(gè)接口類

image

context.inheritFromWidgetOfExactType(FrogColor)其實(shí)是通過context/element往上遍歷樹,查找到第一個(gè)FrogColor的祖先節(jié)點(diǎn),取該節(jié)點(diǎn)的widget對(duì)象

從下往上

子節(jié)點(diǎn)狀態(tài)變更,向上上報(bào)通過發(fā)送通知的方式

  • 定義通知類,繼承至Notification
  • 父節(jié)點(diǎn)使用NotificationListener 進(jìn)行監(jiān)聽捕獲通知
  • 子節(jié)點(diǎn)有數(shù)據(jù)變更調(diào)用下面接口進(jìn)行數(shù)據(jù)上報(bào)
    Notification(data).dispatch(context)

閑魚flutter的界面框架設(shè)計(jì)

image

Layout

Size 計(jì)算

image

parent傳入約束條件,在dramframe的layout階段,child根據(jù)自身的渲染內(nèi)容返回size

問題:在build()階段獲取不到size,很多時(shí)候需要提前知道部分widget size來進(jìn)行布局,解決方案當(dāng)widget 在對(duì)應(yīng)renderobject的layout階段之后,發(fā)送一個(gè)LayoutChangeNotification,參考SizeChangedLayoutNotifier class,但是SizeChangedLayoutNotifier沒有上報(bào)init layout size,可以自己參考這個(gè)實(shí)現(xiàn)封裝一個(gè)Notifier

Offset 計(jì)算

  1. renderObject拿到計(jì)算好的size,再加上一些布局屬性(align、paddig)等,計(jì)算child相對(duì)parent的offset
  2. offset存放在每個(gè)child renderObject的BoxParentData中
  3. 當(dāng)parent擁有mutil children時(shí),BoxParentData還用來存children兄弟節(jié)點(diǎn)之間的遍歷順序

Relayout boundary

renderObject在layout階段做了Relayout boundary的優(yōu)化,當(dāng)子樹進(jìn)行relayout時(shí),滿足下面三種中的一種

  • parentUsesSize == false
  • sizedByParent == true
  • constraints.isTight

那么該renderObject設(shè)置為Relayout boundary,也就是該renderObject的重新layout不觸發(fā)parent的layout,一般情況下開發(fā)人員不需要關(guān)心Relayout boundary,除非是使用CustomMultiChildLayout

Paint

Layer

iOS的每一個(gè)UIView都有一個(gè)layer,flutter的render object不一定存在layer,一般情況下一個(gè)renderObject子樹都渲染在一個(gè)layer上,那么什么renderObject具有l(wèi)ayer,子renderObject怎么渲染到這個(gè)layer?

  1. 當(dāng)一個(gè)renderObject的 alwaysNeedsCompositing == true 或者isRepaintBoundary == true,renderOject會(huì)有對(duì)應(yīng)的compositing layer
  2. 子renderObject會(huì)對(duì)目標(biāo)layer返回對(duì)應(yīng)的offsetLayer, 目標(biāo)compositing layer再根據(jù)offset合成一個(gè)渲染的紋理buffer


    image

Repaint Boundary

類似Relayout boundary,Paint階段也有Repaint Boundary,目的和layout一樣,就是對(duì)應(yīng)子樹的paint不會(huì)導(dǎo)致外部的repaint,但是Relayout boundary需要開發(fā)人員自己設(shè)置,使用RepaintBoundary widget進(jìn)行設(shè)置,ListView在渲染的item默認(rèn)都是使用了RepaintBoundary,顯而易見ListView的children之間都是相互獨(dú)立的。
Flutter建議復(fù)雜的image渲染使用RepaintBoundary,image的渲染需要io操作,然后解碼,最后渲染,使用RepaintBoundary可以進(jìn)行g(shù)pu的緩存,但是不一定就會(huì)緩存,engine會(huì)判斷這個(gè)image是否足夠復(fù)雜,畢竟gpu緩存還是非常珍貴的,同時(shí)RepaintBoundary還會(huì)對(duì)一些反復(fù)渲染的layer進(jìn)行緩存處理(反復(fù)渲染3次及以上,這個(gè)是flutter的視頻中提到的)

結(jié)語

Flutter還處于Beta階段,有些界面編程的接口設(shè)計(jì)還不夠成熟,相比iOS和安卓生態(tài)還很不成熟,需要我們共同的創(chuàng)建,F(xiàn)lutter提供的調(diào)試工具相比一開始接觸的時(shí)候,已經(jīng)完善很多,讓我們給Flutter更多的耐心和包容,期待Flutter越來越完善。
簡(jiǎn)歷投遞:guicai.gxy@alibaba-inc.com

參考資料

  • https://github.com/flutter/flutter
  • https://github.com/flutter/engine
  • https://flutter.io/

本頁內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認(rèn)為本網(wǎng)頁中由涉嫌抄襲的內(nèi)容,請(qǐng)及時(shí)與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會(huì)在5工作日內(nèi)聯(lián)系您,一經(jīng)查實(shí),本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://jstctz.cn/18147.html
相關(guān)APP開發(fā)
 八年  行業(yè)經(jīng)驗(yàn)

多一份參考,總有益處

聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費(fèi)獲得網(wǎng)站建設(shè)方案及報(bào)價(jià)

咨詢相關(guān)問題或預(yù)約面談,可以通過以下方式與我們聯(lián)系

業(yè)務(wù)熱線:余經(jīng)理:13699882642

Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.