為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴
發(fā)表日期:2018-09 文章編輯:小燈 瀏覽次數(shù):5635
Flutter作為一種全新的響應(yīng)式,跨平臺(tái),高性能的移動(dòng)開(kāi)發(fā)框架。從開(kāi)源以來(lái),已經(jīng)得到越來(lái)越多開(kāi)發(fā)者的喜愛(ài)。閑魚(yú)是最早一批與谷歌展開(kāi)合作,并在重要的商品詳情頁(yè)中使用上線的公司。一路走來(lái),積累了大量的開(kāi)發(fā)經(jīng)驗(yàn)。雖然越來(lái)越多的技術(shù)大牛在flutter世界中弄得風(fēng)聲水起,但是肯定有很多的flutter小白希望能快速上手,享受flutter編程的樂(lè)趣。本文就是面向剛剛踏上futter的同學(xué),從Flutter體系中最基本的一個(gè)概念widget入手學(xué)習(xí)Flutter。希望能助力每一位初學(xué)者。
可能大家要問(wèn)的第一個(gè)問(wèn)題是為什么從Widget開(kāi)始?
從flutter的架構(gòu)圖中不難看出widget是整個(gè)視圖描述的基礎(chǔ)。Flutter 的核心設(shè)計(jì)思想便是
即一切即Widget。在flutter的世界里,包括views,view controllers,layouts等在內(nèi)的概念都建立在Widget之上。widget是flutter功能的抽象描述。所以掌握Flutter的基礎(chǔ)就是學(xué)會(huì)使用widget開(kāi)始。
本文會(huì)從大家熟悉的UI繪制視角來(lái)介紹flutter組件和布局的基礎(chǔ)知識(shí)。首先羅列了UI開(kāi)發(fā)中最為常用,最為基礎(chǔ)的組件。下面逐一進(jìn)行介紹。
1.1 Text
Text幾乎是UI開(kāi)發(fā)中最為重要的組件之一了,UI上面文字的展示基本上都要靠Text組件來(lái)完成。Flutter提供了原生的Text組件。Text的配置屬性是很豐富的,屬性主要分為兩個(gè)部分一個(gè)是對(duì)齊&顯示控制相關(guān)的在Text類(lèi)的屬性中,另一類(lèi)是樣式相關(guān)的屬性使用單獨(dú)的類(lèi)TextStyle進(jìn)行控制。跟native控件相比(以android為例),Text的組件基本上提供了同等的能力,并且提供了更加豐富的樣式裝飾能力。詳細(xì)的屬性可以參考官方文檔flutter text.
1.1.1 實(shí)踐Coding
設(shè)置文字&文字大小&顏色&行數(shù)限制&文本對(duì)齊
效果如下:
1.2 Image
圖片也是UI部分開(kāi)發(fā)最為重要的組件之一。在能看圖隨看文字的年代,圖片是頁(yè)面展示的重中之重!Flutter同樣原生提供了Image組件。下面重點(diǎn)介紹一下幾個(gè)重點(diǎn):
1.2.1 縮放
怎樣設(shè)置圖片顯示的縮放方式呢?
Flutter中的圖片縮放是fit字段來(lái)控制的。這是對(duì)最終圖片展示效果影響很大的一個(gè)參數(shù),也是容易出錯(cuò)的點(diǎn)。下面逐個(gè)分析一下flutter Image組件提供的縮放方式。
縮放屬性值在BoxFit枚舉中
下面列出的圖片是flutter官方對(duì)各種縮放做的圖片示例?;旧隙急硎龊芮宄?,就整理出來(lái)供大家查閱。
1.2.2 圖片獲取
怎樣從各種來(lái)源加載圖片?
默認(rèn)的Image組件不能直接顯示圖片,他需要一個(gè)ImageProvider來(lái)提供具體的圖片資源的(即Image中的image字段需要賦值)。咋一看這確實(shí)非常麻煩,但是實(shí)際上ImageProvider并不需要完全重新自己實(shí)現(xiàn)。在Image類(lèi)中目前提供了一下幾個(gè)實(shí)現(xiàn)好的ImageProvider,基本能滿足常見(jiàn)的需求。
Image同樣支持GIF圖片
網(wǎng)絡(luò)請(qǐng)求Image是大家最常見(jiàn)的操作。這里重點(diǎn)說(shuō)明兩個(gè)點(diǎn):
? 緩存
ImageCache是ImageProvider默認(rèn)使用的圖片緩存。ImageCache使用的是LRU的算法。默認(rèn)可以存儲(chǔ)1000張圖片。如果覺(jué)得緩存太大,可以通過(guò)設(shè)置ImageCache的maximumSize屬性來(lái)控制緩存圖片的數(shù)量。也可以通過(guò)設(shè)置maximumSizeBytes來(lái)控制緩存的大?。J(rèn)緩存大小10MB)。
??CDN優(yōu)化
如果想要使用cdn優(yōu)化,可以通過(guò)url增加后綴的方式實(shí)現(xiàn)。默認(rèn)實(shí)現(xiàn)中沒(méi)有這個(gè)點(diǎn),但是考慮到cdn優(yōu)化的可觀收益,建議大家利用好這個(gè)優(yōu)化。
1.2.3 FadeInImage
在實(shí)際開(kāi)發(fā)中,考慮到圖片加載速度可能不能達(dá)到預(yù)期。所以希望能增加漸入效果&增加placeHolder的功能。Flutter同樣提供的這樣的組件——FadeInImage。
FadeInImage也提供了從多種渠道加載圖片的能力。這塊跟上面所說(shuō)差異不大。這里不再贅述。
1.2.4 實(shí)踐Coding
??從網(wǎng)絡(luò)獲取圖片保持圖片比例并盡可能大的放入
??效果如下:
1.3 Container
Flutter的設(shè)計(jì)思想就是完全的widget化。這也就是說(shuō)連最基本的padding,Center都是widget。設(shè)想一下如果每次寫(xiě)view,連padding,Center都要自己包一個(gè)組件是一種怎樣的體驗(yàn)?作為一個(gè)工程師,別給只給我談思想,實(shí)際操作的操作效率也同樣非常重要。flutter 官方也意識(shí)到了這個(gè)問(wèn)題,他們從實(shí)際編寫(xiě)效率的角提供了一個(gè)友好高效的封裝,這就是Container!首先沒(méi)有任何疑問(wèn),Container 本身也是一個(gè)widget。但是他卻提供了對(duì)基礎(chǔ)widget的封裝,提高了UI基礎(chǔ)裝飾能力的表達(dá)效率。Container類(lèi)似于android中的ViewGroup。
相信大部分的屬性大家都會(huì)感覺(jué)非常親切,結(jié)合代碼注釋都比較容易理解,這里就不再贅述。其中需要重點(diǎn)解釋一下的是:Decoration和BoxConstraints。
1.3.1 裝飾
Decoration是對(duì)Container進(jìn)行裝飾的描述。其概念類(lèi)似與android中的shape。一般實(shí)際場(chǎng)景中會(huì)使用他的子類(lèi)BoxDecoration。BoxDecoration提供了對(duì)背景色,邊框,圓角,陰影和漸變等功能的定制能力。
需要注意幾個(gè)點(diǎn):
??BoxDecoration的image屬性相當(dāng)于設(shè)置的是背景圖。但是image會(huì)繪制在color 和gradient之上。
??image是需要一個(gè)DecorationImage類(lèi)的實(shí)現(xiàn)。DecorationImage的屬性和Image組件比較類(lèi)似,可以復(fù)用Image組件中的ImageProvider。
1.3.2 大小
BoxConstraints其實(shí)是對(duì)Container組件大小的描述。BoxConstraints屬性比較簡(jiǎn)單。如果不太清楚可以研究一下盒子模型。這里有個(gè)點(diǎn)需要重點(diǎn)說(shuō)明一下:
??如何表達(dá)盡可能大這樣的意思?(類(lèi)似于android中的match_parent)Flutter中可以使用double.infinity來(lái)做出類(lèi)似的表達(dá)。
1.3.3 實(shí)踐Coding
??設(shè)置邊框&padding&margin&圓角&背景圖
??效果如下:
1.4 手勢(shì)操作
手勢(shì)操作是最常見(jiàn)的UI交互操作。在Flutter中手勢(shì)識(shí)別也是一個(gè)widget!這點(diǎn)對(duì)新人來(lái)說(shuō)又是一個(gè)新鮮的地方。通常來(lái)說(shuō)可以通過(guò)GestureDetector類(lèi)來(lái)完成點(diǎn)擊事件的處理。使用時(shí)只需要將GestureDetector包裹在目標(biāo)widget外面,再實(shí)現(xiàn)對(duì)應(yīng)事件的函數(shù)即可。從點(diǎn)擊到長(zhǎng)按,從縮放到拖動(dòng),這個(gè)類(lèi)基本上都有相應(yīng)的實(shí)現(xiàn)。具體可以參見(jiàn)組件文檔。
頁(yè)面布局應(yīng)該是UI編寫(xiě)最為根本的知識(shí),其主要的描述的是父子組件子子組件之間的位置關(guān)系。首先我們理解一下官方文檔的邏輯:
將布局分為單孩子和多孩子是Flutter布局的一大特色。這點(diǎn)對(duì)native研發(fā)同學(xué)來(lái)說(shuō)會(huì)比較新鮮。單孩子組件主要繼承自SingleChildRenderObjectWidget。這些組件能提供豐富的裝飾能力(例如container),也能提供部分特定的布局能力(例如center)。多孩子組件繼承自MultiChildRenderObjectWidget,能提供更加豐富的布局能力(Flex,Stack,flow),但幾乎沒(méi)有裝飾的能力。下面介紹幾個(gè)重點(diǎn)布局:
2.1 Flex
Flutter在布局上也提供了完整的Flex布局能力。但是在Flutter官方文檔中Layout Widgets,是看不到任何Flex的影子的。映入眼簾的卻是Row,Column,這些是什么鬼?其實(shí)不難發(fā)現(xiàn)類(lèi)似Row,Column 這樣的組件,他們的基類(lèi)都是Flex。Row和Column差別是設(shè)置了不同的flex-direction。而之所這么設(shè)計(jì),是因?yàn)镕lutter的widget從開(kāi)始設(shè)計(jì)之初就考慮到UI布局語(yǔ)義保持的重要性。這塊應(yīng)該部分借鑒了前端的經(jīng)驗(yàn),極力避免一個(gè)div搞定全部頁(yè)面的尷尬(當(dāng)然flutter也可以使用Flex來(lái)做同樣的事情,但是并不建議這么做)。
Flutter使用的Flex模型基本上跟傳統(tǒng)的Css類(lèi)似。這塊前端同學(xué)可以快速上手。但是Flex對(duì)于客戶端同學(xué)來(lái)說(shuō)是一種全新的布局方式。Flex的基礎(chǔ)知識(shí)可以參看flex布局基礎(chǔ)。由于篇幅有限這里不展開(kāi)敘述。這里只重點(diǎn)強(qiáng)調(diào)一個(gè)點(diǎn):
如下圖flex布局概念如下:
flex通過(guò)direction設(shè)置了flex的主軸方向即main axis。和主軸垂直的方向叫做cross axis。flex布局中對(duì)子布局的控制是從main axis 和cross axis兩個(gè)方向上進(jìn)行的。例如居中有main axis居中和cross axis居中。兩者都居中才是容器的完全居中。這點(diǎn)是客戶端同學(xué)可能會(huì)容易弄混的地方。重點(diǎn)關(guān)注一下。
2.1.1 實(shí)踐Coding
ok,看完這些知識(shí),我們實(shí)際需求角度實(shí)際操作幾個(gè)case來(lái)熟悉一下Flex。
??居中
??效果如下:
??weight left:right=2:1 通過(guò)設(shè)置Flexible的flex值大小完成比例設(shè)置
??效果如下:
2.2 stack
在實(shí)際開(kāi)發(fā)中,還是需要在一些Widgets的上面再覆蓋上新的Widgets。這時(shí)候就需要層式布局了。這種布局在Native上,以android為例,類(lèi)似于relativeLayout 或者FrameLayout。在Flutter中使用的是Stack。
實(shí)際使用中Stack中的子Widgets分為兩種:
??positioned
是包裹在組件Positioned中的組件
可以通過(guò)Positioned屬性靈活定位
??non-positioned
沒(méi)有包裹在Positioned組件中
需要通過(guò)父Widget Stack 的屬性來(lái)控制布局
對(duì)于non-positioned children, 我們通過(guò)控制Stack的alignment屬性來(lái)控制對(duì)齊方式。Positioned的布局方式類(lèi)似于H5&weex中的position布局中的absolute布局方式。通過(guò)設(shè)置距離父組件上下左右的距離,Positioned對(duì)象能在Stack布局中更加靈活的控制view的展現(xiàn)方式。
2.2.1 實(shí)踐Coding
??層疊布局
??效果如下:
當(dāng)你看完Flutter Widge文檔的時(shí)候,我們突然發(fā)現(xiàn)一個(gè)略顯尷尬的問(wèn)題:組件是否顯示怎么控制?貌似所有的組件中都沒(méi)有這個(gè)屬性!這不坑了,咋辦?
目前看方法無(wú)非如下幾個(gè):
3.1 刪除法
核心將該真實(shí)widget或者widget樹(shù)從renderTree中移除。
具體到實(shí)踐級(jí)別主要分為兩個(gè):
??單個(gè)組件‘隱藏’自己。在build方法中返回一個(gè)空的Container.
??多個(gè)child
在父容器的children字段的list中,刪除掉對(duì)應(yīng)的cell。
3.2 Offstage
Offstage 是一個(gè)widget。Offstage的offstage屬性設(shè)置為true,那么Offstage以及他的child都將不會(huì)被繪制到界面上。
sample code如下:
3.3 透明度
設(shè)置widget的透明度,使之不可見(jiàn)。但是這樣的方法是副作用的。因?yàn)檫@個(gè)對(duì)應(yīng)的widget樹(shù)是已經(jīng)經(jīng)過(guò)了完整的layout&paint過(guò)程,成本高。同時(shí)設(shè)置透明度本身也要耗費(fèi)一定的計(jì)算資源,造成了二次浪費(fèi)。需要注意的是即便變透明了,占據(jù)的位置還在。大家慎重選擇使用。
sample code如下:
visibility的控制還是比較麻煩的。這是Flutter設(shè)計(jì)上不符合正常習(xí)慣的一個(gè)點(diǎn),需要大家重點(diǎn)關(guān)注。
4.1 state 生命周期
widget是immutable的,發(fā)生變化的時(shí)候需要重建,所以談不上狀態(tài)。StatefulWidget 中的狀態(tài)保持其實(shí)是通過(guò)State類(lèi)來(lái)實(shí)現(xiàn)的。State擁有一套自己的生命周期,下面做一個(gè)簡(jiǎn)單的介紹。
生命周期狀態(tài)圖如下:
幾個(gè)注意點(diǎn)
??didChangeDependencies有兩種情況會(huì)被調(diào)用。
創(chuàng)建時(shí)候在initState 之后被調(diào)用
在依賴的InheritedWidget發(fā)生變化的時(shí)候會(huì)被調(diào)用
??正常的退出流程中會(huì)執(zhí)行deactivate然后執(zhí)行dispose。但是也會(huì)出現(xiàn)deactivate以后不執(zhí)行dispose,直接加入樹(shù)中的另一個(gè)節(jié)點(diǎn)的情況。
??這里的狀態(tài)改變包括兩種可能:1.通過(guò)setState內(nèi)容改變 2.父節(jié)點(diǎn)的state狀態(tài)改變,導(dǎo)致孩子節(jié)點(diǎn)的同步變化。
4.2 App生命周期
需要指出的是如果想要知道App的生命周期,那么需要通過(guò)WidgetsBindingObserver的didChangeAppLifecycleState 來(lái)獲取。通過(guò)該接口可以獲取是生命周期在AppLifecycleState類(lèi)中。常用狀態(tài)包含如下幾個(gè):
一個(gè)實(shí)際場(chǎng)景中的例子:
在不考慮suspending的情況下:從后臺(tái)切入前臺(tái)生命周期變化如下:
??AppLifecycleState.inactive->AppLifecycleState.resumed;
從前臺(tái)壓后臺(tái)生命周期變化如下:
??AppLifecycleState.inactive->AppLifecycleState.paused;
5.1 為什么使用dart語(yǔ)言?
Dart語(yǔ)言對(duì)大部分開(kāi)發(fā)者而言是很陌生的一種語(yǔ)言。google為啥會(huì)選擇如此'冷門(mén)'的語(yǔ)言來(lái)開(kāi)發(fā)flutter?主要原因如下:
??dart具有jit&Aot雙重編譯執(zhí)行方式。這樣就能利用JIt進(jìn)行開(kāi)發(fā)階段的hot reload開(kāi)發(fā),提升研發(fā)效率。同時(shí)在最終release版本中使用aot將dart代碼直接變成目標(biāo)平臺(tái)的指令集代碼。簡(jiǎn)單高效,最大限度保障了性能。
??dart針對(duì)flutter中頻繁創(chuàng)建銷(xiāo)毀Widget的場(chǎng)景做了專門(mén)的gc優(yōu)化。通過(guò)分代無(wú)鎖垃圾回收器,將gc對(duì)性能的影響降至最低。
??dart語(yǔ)言在語(yǔ)法上面是類(lèi)java的,易學(xué)易用。
5.2 為什么widget都是immutable?
個(gè)人認(rèn)為是兩個(gè)主要的點(diǎn):
??提高渲染效率
flutte在頁(yè)面渲染上面的核心思想是simple is fast!將widget設(shè)計(jì)成immutable,所以在數(shù)據(jù)變化時(shí),flutter選擇重建widget樹(shù)的方式進(jìn)行數(shù)據(jù)更新。采用這樣方式的好處是框架不需要關(guān)心數(shù)據(jù)影響的范圍,簡(jiǎn)單高效。缺點(diǎn)就是對(duì)GC會(huì)造成壓力。
??組件描述的復(fù)用
既然widget都是不可變的。那widget可以以較低成本進(jìn)行復(fù)用。在一個(gè)真實(shí)的渲染樹(shù)中可能存在同一個(gè)widget渲染樹(shù)中不同節(jié)點(diǎn)的情況。
5.3 widget是view么?
可能剛開(kāi)始接觸flutter的同學(xué)最疑惑的一個(gè)問(wèn)題就是widget和view的關(guān)系了。那么簡(jiǎn)單分析一下:
widget是對(duì)頁(yè)面UI的一種描述。他功能類(lèi)有點(diǎn)似于android中的xml,或者web中的html。widget在渲染的時(shí)候會(huì)轉(zhuǎn)化成element。Element相比于widget增加了上下文的信息。element是對(duì)應(yīng)widget,在渲染樹(shù)的實(shí)例化節(jié)點(diǎn)。由于widget是immutable的,所以同一個(gè)widget可以同時(shí)描述多個(gè)渲染樹(shù)中的節(jié)點(diǎn)。但是Element是描述固定在渲染書(shū)中的某一個(gè)特定位置的點(diǎn)。簡(jiǎn)單點(diǎn)說(shuō)widget作為一種描述是可以復(fù)用的,但是element卻跟需要繪制的節(jié)點(diǎn)一一對(duì)應(yīng)。那element是最終渲染的view么?抱歉,還不是。element繪制時(shí)會(huì)轉(zhuǎn)化成rendObject。RendObject才是真正經(jīng)過(guò)layout和paint并繪制在屏幕上的對(duì)象。在flutter中有三套渲染相關(guān)的tree,分別是:widget tree, element tree & rendObject tree。三者的渲染流程如下:
那可能有人會(huì)問(wèn),為什么需要增增加中間這層的Element tree?
flutter是響應(yīng)式的框架。在某一時(shí)刻頁(yè)面的布局,可能受不同的輸入源的影響。Element這層實(shí)際上做了對(duì)某一時(shí)刻事件的匯總,在將真正需要修改的部分同步到真實(shí)的rendObject tree上。這么做有兩個(gè)好處:
1.不需要直接操作UI,改為通過(guò)數(shù)據(jù)驅(qū)動(dòng)視圖。代碼表達(dá)可以更加精煉。
2.最大層度降低對(duì)最終真實(shí)視圖(rendObject tree)的修改,提高頁(yè)面渲染效率。
5.4 StatelessWidget 和 StatefulWidget的區(qū)別
StatelessWidget是狀態(tài)不可變的widget。初始狀態(tài)設(shè)置以后就不可再變化。如果需要變化需要重新創(chuàng)建。StatefulWidget可以保存自己的狀態(tài)。那問(wèn)題是既然widget都是immutable的,怎么保存狀態(tài)?其實(shí)Flutter是通過(guò)引入了State來(lái)保存狀態(tài)。當(dāng)State的狀態(tài)改變時(shí),能重新構(gòu)建本節(jié)點(diǎn)以及孩子的Widget樹(shù)來(lái)進(jìn)行UI變化。注意:如果需要主動(dòng)改變State的狀態(tài),需要通過(guò)setState()方法進(jìn)行觸發(fā),單純改變數(shù)據(jù)是不會(huì)引發(fā)UI改變的。
本文詳細(xì)解釋了基礎(chǔ)組件的用法,也解答了一些初學(xué)者的疑惑。希望能給剛踏上flutter學(xué)習(xí)之路的人一些幫助。如果對(duì)文本的內(nèi)容有疑問(wèn)或指正,歡迎告知我們。
閑魚(yú)技術(shù)團(tuán)隊(duì)是一只短小精悍的工程技術(shù)團(tuán)隊(duì)。我們不僅關(guān)注于業(yè)務(wù)問(wèn)題的有效解決,同時(shí)我們?cè)谕苿?dòng)打破技術(shù)棧分工限制(android/iOS/Html5/Server 編程模型和語(yǔ)言的統(tǒng)一)、計(jì)算機(jī)視覺(jué)技術(shù)在移動(dòng)終端上的前沿實(shí)踐工作。作為閑魚(yú)技術(shù)團(tuán)隊(duì)的軟件工程師,您有機(jī)會(huì)去展示您所有的才能和勇氣,在整個(gè)產(chǎn)品的演進(jìn)和用戶問(wèn)題解決中證明技術(shù)發(fā)展是改變生活方式的動(dòng)力。
1.https://flutter.io/widgets-intro/
2.https://flutter.io/technical-overview/
3.css 盒子模型簡(jiǎn)介
4.flutter Layout Widgets目錄
5.flex布局基礎(chǔ)
6.A Visual Guide to CSS3 Flexbox Properties
7.為什么說(shuō)Flutter是革命性的?
8.深入了解Flutter界面開(kāi)發(fā)
本文作者:閑魚(yú)技術(shù)-意境
閱讀原文
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
日期:2018-10 瀏覽次數(shù):7393
日期:2018-12 瀏覽次數(shù):4465
日期:2018-07 瀏覽次數(shù):4996
日期:2018-12 瀏覽次數(shù):4295
日期:2018-09 瀏覽次數(shù):5634
日期:2018-12 瀏覽次數(shù):10042
日期:2018-11 瀏覽次數(shù):4939
日期:2018-07 瀏覽次數(shù):4708
日期:2018-05 瀏覽次數(shù):4982
日期:2018-12 瀏覽次數(shù):4438
日期:2018-10 瀏覽次數(shù):5261
日期:2018-12 瀏覽次數(shù):6335
日期:2018-11 瀏覽次數(shù):4589
日期:2018-08 瀏覽次數(shù):4715
日期:2018-11 瀏覽次數(shù):12793
日期:2018-09 瀏覽次數(shù):5710
日期:2018-12 瀏覽次數(shù):4959
日期:2018-10 瀏覽次數(shù):4304
日期:2018-11 瀏覽次數(shù):4653
日期:2018-12 瀏覽次數(shù):6181
日期:2018-06 瀏覽次數(shù):4126
日期:2018-08 瀏覽次數(shù):5570
日期:2018-10 瀏覽次數(shù):4578
日期:2018-12 瀏覽次數(shù):4657
日期:2018-07 瀏覽次數(shù):4485
日期:2018-12 瀏覽次數(shù):4634
日期:2018-06 瀏覽次數(shù):4510
日期:2018-11 瀏覽次數(shù):4491
日期:2018-12 瀏覽次數(shù):4379
日期:2018-12 瀏覽次數(shù):5391
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.