Flutter中自定義組件一般有兩種方式:
成都創(chuàng)新互聯(lián)公司致力于互聯(lián)網(wǎng)網(wǎng)站建設(shè)與網(wǎng)站營銷,提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、網(wǎng)站開發(fā)、seo優(yōu)化、網(wǎng)站排名、互聯(lián)網(wǎng)營銷、重慶小程序開發(fā)公司、公眾號(hào)商城、等建站開發(fā),成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)策劃專家,為不同類型的客戶提供良好的互聯(lián)網(wǎng)應(yīng)用定制解決方案,幫助客戶在新的全球化互聯(lián)網(wǎng)環(huán)境中保持優(yōu)勢。
CustomPaint繼承自SingleChildRenderObjectWidget,即它可以在通過嵌套引入到widget樹中,并且可以有一個(gè)child子widget。它的構(gòu)造方法如下:
painter和foregroundPainter需要接收CustomPainter對象,是CustomPaint核心。CustomPainter是進(jìn)行UI繪制的核心類,繪制時(shí), CustomPaint 首先在畫布上調(diào)用 painter繪制 , 然后再繪制它的 child Widget, child 繪制完成后再調(diào)用 foregroundPainter 進(jìn)行繪制。
size屬性標(biāo)識(shí)繪制區(qū)域大小,但當(dāng)CustomPaint有child,該屬性將會(huì)忽略,而使用child的大小為繪制區(qū)域大小。
isComplex和willChange用于控制繪制層緩存處理的,這里暫不討論。
可實(shí)現(xiàn)CustomPainter子類進(jìn)行UI繪制
實(shí)現(xiàn)paint方法進(jìn)行真正的繪制,canvas是畫布對象,size是繪制區(qū)域,是從CustomPaint中size屬性傳遞得到的。繪制過程與Android原生開發(fā)十分類似,連API都十分相像,這點(diǎn)對熟悉Android原生開發(fā)者真是太友好了。
Paint對象是畫筆對象,就是繪圖工具,我們可以設(shè)置畫筆的顏色、粗細(xì)、是否抗鋸齒、筆觸形狀以及作畫風(fēng)格等,通過這些屬性我們可以很方便的來定制自己的UI效果,在繪制的過程中可以定義多個(gè)畫筆,以便實(shí)現(xiàn)多種風(fēng)格圖形的集合。
根據(jù)需求選擇合適的畫筆屬性,完成你的繪制。
Canvas是繪制的畫布,它包含了很多繪制方法,可以繪制出各種形狀的圖形。需要注意的是,畫布是應(yīng)用所有控件都在使用的, 所以通過這個(gè)畫布其實(shí)是可以繪制充滿屏幕的內(nèi)容的,每次繪制都應(yīng)該限制在本控件的區(qū)域(Size)內(nèi), 以免繪制覆蓋到其他組件。
下面介紹下Canvas的繪制方法:
PointMode是個(gè)枚舉
p1、p2為線段兩個(gè)端點(diǎn)
Rect定義矩形的大小位置,有多種構(gòu)造方式:
RRect描述圓角矩形,他通過Rect和Radius來構(gòu)造
畫圓比較簡單,c表示圓心位置,radius是半徑。
橢圓使用外接矩形確定大小位置,rect就是外接矩形。
繪制弧形,先確定弧形對應(yīng)的橢圓,同樣地用外接矩形rect確定橢圓,然后根據(jù)起始點(diǎn)和結(jié)束點(diǎn)角度來確定那一段弧度,startAngle,sweepAngle分別代表起始和結(jié)束點(diǎn)角度,角度用弧度表示法。
useCenter表示是否連接閉合形狀,userCenter = false表示不閉合,即畫一段弧線,userCenter = true表示閉合,即繪制一個(gè)扇形。
繪制路徑,關(guān)鍵在于構(gòu)建路徑Path,可以直接new Path對象,然后通過path方法可以連接出圖形,path關(guān)鍵方法如下:
還有其他方法,有興趣可以查看API。
我是初學(xué)者小白,所以很多看法不深,理解也不夠透徹。但是很適合小白們一起從低角度往高處探索。文中有錯(cuò)誤的,感謝指正,一起進(jìn)步。
趁著假期做一個(gè)Flutter的地圖功能,因?yàn)楹蠖诉x用了百度地圖,所以前端沒得挑。找了遍插件,并沒有現(xiàn)成可用的。(不過發(fā)現(xiàn)了百度官方也自開發(fā)Flutter插件,目前功能只有一個(gè)獲取本地位置信息,后期會(huì)繼續(xù)增加吧?很期待?。?/p>
參考帖子:
這個(gè)實(shí)際上跟功能之間沒太大關(guān)系,只是我按照個(gè)人摸索的過程來寫。
當(dāng)對一個(gè)“領(lǐng)域/知識(shí)塊”完全不懂的時(shí)候,360°的方向都不確定的話。先了解基礎(chǔ)概念,有利于你確定自己的摸索方向。
參考帖子:
中間我跳過了幾十,上百個(gè)帖子的摸索過程。這個(gè)才是關(guān)鍵能夠真正做事的參考。
因?yàn)榘俣鹊膕dk還算是很完善的,所以一旦出問題,都會(huì)有對應(yīng)的報(bào)錯(cuò)提示。
我是使用flutter插件:permission_handler,來解決安卓的動(dòng)態(tài)授權(quán)問題,用法簡單而且設(shè)計(jì)合理。
這個(gè)錯(cuò)誤直接來看,就是簽名有問題。怎么查看SHA1碼和包名,這里不多說,網(wǎng)上有極其多的方法,百度Sdk開發(fā)指南里也有。沒那么復(fù)雜,也沒那么麻煩。按照流程操作就是對的。
實(shí)在不放心?跟我一樣,flutter打包后,把a(bǔ)pk反過來解SHA1碼不就行了?
參考帖子:
紅色框框基本就是帖子講解的那樣。
藍(lán)色框框見下圖:release標(biāo)簽里好像是自己設(shè)置了。所以debug標(biāo)簽里面,箭頭指向的位置,是我多設(shè)置的一個(gè)參數(shù)。
uid: -1 appid -1 msg: httpsPost failed,IOException:Unable to resolve host "api.map.baidu.com": No address associated with hostname
這一步我是哭笑不得,一開始老是和問題(2)混淆,導(dǎo)致浪費(fèi)很多時(shí)間。仔細(xì)閱讀后,發(fā)現(xiàn)是不能連接到“api.map.baidu.com”。
我打開模擬器的chrome瀏覽器,發(fā)現(xiàn)不能上網(wǎng)。查看手機(jī)的dns是10.0.2.3(默認(rèn)的),和家里wifi不一樣,所以不能上網(wǎng)也正常,之前居然沒發(fā)現(xiàn)這個(gè)問題!?。?/p>
終端執(zhí)行:adb shell? 和? getprop,就可以查看所有的屬性參數(shù)了。(window小伙伴自行百度,這個(gè)沒多大差別。如果你有多個(gè)設(shè)備,記得自己選好設(shè)備。)
在里面找到這一項(xiàng),就是你的dns參數(shù)。有些人是net.dns1,我的是net.eth0.dns1。這個(gè)沒關(guān)系,只是等下指令 稍微改動(dòng) 就行。
修改dns指令:setprop net.eth0.dns1 192.168.2.1
后面的192.168.2.1是我自己的dns,這個(gè)根據(jù)自己的情況來填寫。不懂的百度下怎么查看自己的dns。
雖然提示設(shè)置失敗,但是回到模擬器一看,地圖已經(jīng)顯示出來了。
嘿嘿,在flutter設(shè)定多大的區(qū)域,地圖就是多大的區(qū)域。用起來就很方便了。
過程十分痛苦,因?yàn)閷lutter不是很熟悉,對Android原生更是了解很少。所以自己就像突然不能講話,被丟到一個(gè)陌生的環(huán)境,卻要我去找一個(gè)人。所以細(xì)心很重要,一定要看清楚錯(cuò)誤提示,不要錯(cuò)過每一個(gè)細(xì)節(jié)和可能性。
幸好最后解決了問題,開心~
其實(shí)如果你仔細(xì)閱讀過百度官方的文檔,會(huì)發(fā)現(xiàn)里面有關(guān)于 地圖的生命周期管理 。然后在這里面沒有提及到,這一點(diǎn)雖然沒提,但不可或缺,小伙伴就自行思考吧。
最后還有一點(diǎn),其實(shí)我的初衷是想實(shí)現(xiàn)一個(gè)百度地圖的plugin,但是苦于能力有限,對Android的不熟悉,最后折戟。我不得已另起項(xiàng)目,然后重新實(shí)現(xiàn)地圖sdk接入。經(jīng)過這次對于這些有更多更全面的認(rèn)知后,有空會(huì)再次研究flutter 插件的開發(fā),共勉,奧利給?。?!
相對于iOS開發(fā),F(xiàn)lutter的布局更具有靈活性,每個(gè)頁面設(shè)計(jì)都不一樣,相同頁面可選擇的布局方式也不一樣,如果單純的說應(yīng)該如何去布局,我覺得不現(xiàn)實(shí),大家可以參考下 Flutter官方的布局教程 。接下來,筆者,通過項(xiàng)目中的一個(gè)頁面,來一步一步的拆解布局的流程。整個(gè)過程,基本上按照拆解、組件封裝、具體布局這三步來的。
根據(jù)設(shè)計(jì)圖,可以看出整體可以分成兩部分,上面一部分是系統(tǒng)介紹模塊,下面一部分是真正的登錄內(nèi)容,因?yàn)樯婕暗蒋B加,因此考慮用Stack;
系統(tǒng)介紹模塊部分:整體也是涉及到疊加,考慮用Stack,分為四部分。最底部漸變色背景用一個(gè)contanier,無須指定位置,全視圖擴(kuò)展;載放logo圖標(biāo)在上一層,用Image。最后兩個(gè)Text同級放在最上層。Image,Text各用Positioned包裹去指定位置。
登錄內(nèi)容模塊是最外層是一個(gè)Contanier容器,去控制背景色和圓角。然后是一個(gè)Column元素,逐行排列。
第一行為Image,
第二行為Text,
第三行可以看成一個(gè)小Column,分兩塊進(jìn)行布局
第四行可以看成一個(gè)小Column,分兩塊進(jìn)行布局
第五行可以看作一個(gè)TextButton,
第六行可以看作一個(gè)Row,分三塊進(jìn)行布局
通過上面這樣一步一步的分析后,基本上對大致的布局有了一個(gè)了解,最外層的控件大致選對(只要能實(shí)現(xiàn)的話,就是復(fù)雜度以及效率的問題),然后一步一步的拆解每一行的元素,如果有重復(fù)的或者覺得可以封裝出來的部分,則進(jìn)行下一步。
每一行的拆解,大致也是按照這個(gè)思路來進(jìn)行,因此筆者在這里就不做講解了。
在做到第三第四行的時(shí)候,發(fā)現(xiàn)這兩個(gè)很相似,而且設(shè)計(jì)到一些交互邏輯,筆者就想對第三第四行的這種展示進(jìn)行封裝,覺得今后的布局可能會(huì)用到,因此在這一步,可以先把這一塊兒抽離出一個(gè)控件。利用TextField來實(shí)現(xiàn)這種輸入操作,具體的實(shí)現(xiàn)筆者不再詳細(xì)的描述了。
經(jīng)過這一步,整體的規(guī)劃設(shè)計(jì)圖已經(jīng)有了,各個(gè)組件也都有了,接下來的工作就是組裝了。
具體布局設(shè)計(jì)到一些細(xì)節(jié)的地方,例如整體Column的居中對齊(crossAxisAlignment)、間隔(Padding或Container包裹,筆者更喜歡用SizedBox占位)、居左居右居中(Align)、點(diǎn)擊事件(GestureDetector)以及圓角(BorderRadius)等一些特殊情況。
像第六行row是放在底部的,就可以在第六行前面增加一個(gè)Spacer()去填充空白區(qū)域。
對文字顏色大小等,可以用TextStyle直接設(shè)置。
對于輸入框的刪除按鈕,可以用Offstage這種Flutter特有的控制顯示隱藏的控件。
Row:在水平方向上排列子widget的列表。
Column:在垂直方向上排列子widget的列表。
注意:這兩個(gè)屬于多子節(jié)點(diǎn)空間,可以將children排列成一行/一列,但是自身不帶滾動(dòng)屬性,如果超出了一行,在debug下面則會(huì)顯示溢出的提示。
MainAxisAlignment:主軸方向上的對齊方式,會(huì)對child的位置起作用,默認(rèn)是start。
其中MainAxisAlignment枚舉值:
center:將children放置在主軸的中心;
end:將children放置在主軸的末尾;
spaceAround:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,但是首尾child的空白區(qū)域?yàn)?/2;
spaceBetween:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,首尾child都靠近首尾,沒有間隙;
spaceEvenly:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,包括首尾child;
start:將children放置在主軸的起點(diǎn);
其中spaceAround、spaceBetween以及spaceEvenly的區(qū)別,就是對待首尾child的方式。其距離首尾的距離分別是空白區(qū)域的1/2、0、1。
MainAxisSize:在主軸方向占有空間的值,默認(rèn)是max。
MainAxisSize的取值有兩種:
max:根據(jù)傳入的布局約束條件,最大化主軸方向的可用空間;
min:與max相反,是最小化主軸方向的可用空間;
CrossAxisAlignment:children在交叉軸方向的對齊方式,與MainAxisAlignment略有不同。
CrossAxisAlignment枚舉值有如下幾種:
baseline:在交叉軸方向,使得children的baseline對齊;
center:children在交叉軸上居中展示;
end:children在交叉軸上末尾展示;
start:children在交叉軸上起點(diǎn)處展示;
stretch:讓children填滿交叉軸方向;
TextDirection:阿拉伯語系的兼容設(shè)置,一般無需處理。
VerticalDirection:定義了children擺放順序,默認(rèn)是down。
VerticalDirection枚舉值有兩種:
down:從top到bottom進(jìn)行布局;
up:從bottom到top進(jìn)行布局。
top對應(yīng)Row以及Column的話,就是左邊和頂部,bottom的話,則是右邊和底部。
TextBaseline:使用的TextBaseline的方式,有兩種,前面已經(jīng)介紹過。
這個(gè)是Row/Column的內(nèi)的小控件,可以用來實(shí)現(xiàn)權(quán)重的布局
這邊使用一個(gè)Container,里面是Row,使用Expanded對子節(jié)點(diǎn)進(jìn)行權(quán)重處理,如果不使用Expanded,直接放入其他控件也是可以的,只是無法設(shè)置權(quán)重
對于內(nèi)容過長的時(shí)候,會(huì)有溢出提示:
MainAxisAlignment.center:將children放置在主軸的中心;
MainAxisAlignment.start:將children放置在主軸的起點(diǎn);
MainAxisAlignment.end:將children放置在主軸的末尾;
MainAxisAlignment.spaceAround:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,但是首尾child的空白區(qū)域?yàn)?/2;
MainAxisAlignment.spaceBetween:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,首尾child都靠近首尾,沒有間隙;
MainAxisAlignment.spaceEvenly:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,包括首尾child;
下一章我們學(xué)習(xí)基礎(chǔ)組件之Image
zone.js:
scheduleMicrotask執(zhí)行流程不一定是一下流程,此處只是舉例其中一種情況說明Zone的作用
假設(shè)scheduleMicrotask方法在zoneA中執(zhí)行,則scheduleMicrotask方法的大致執(zhí)行流程如下: