flutter操作技巧,簡(jiǎn)書(shū) flutter

Flutter 開(kāi)發(fā)筆記

下面這種情況下,為 InkWell 設(shè)置的 splashColor 不會(huì)生效:

創(chuàng)新互聯(lián)建站堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:做網(wǎng)站、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的邗江網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

需要用 Material 去除背景色,然后將顏色設(shè)置在 InkWell 外部:

在 Dialog builder 中使用 WillPopScope 禁用返回鍵返回:

注意:使用此方法同時(shí)也會(huì)禁用 iOS 上的手勢(shì)滑動(dòng)返回功能,推薦判斷平臺(tái)后再使用。

修改對(duì)話框中的復(fù)選框狀態(tài),最簡(jiǎn)便的方法是通過(guò) Element 中的 markNeedsBuild 方法:

當(dāng)然,更推薦的做法是通過(guò) StatefulBuilder ,然后就可以在 Dialog 中調(diào)用 setState 方法了,不過(guò)在調(diào)用 setState 時(shí)需要判斷 Dialog 是否已經(jīng)關(guān)閉,否則會(huì)造成 setState() called after dispose() 的錯(cuò)誤,可以通過(guò)添加一個(gè)標(biāo)志位來(lái)解決,如下:

在 Web 中加載網(wǎng)絡(luò)圖片有時(shí)會(huì)失敗,遇到這樣的報(bào)錯(cuò): Exception caught by image resource service... ,造成該錯(cuò)誤的原因通常是,圖片跨域了(見(jiàn) 跨域資源共享 )。最簡(jiǎn)單的解決辦法是, 使用 HTML 渲染加載 ,而不是默認(rèn)的 CanvasKit。

Flutter 中所有的 list 默認(rèn)都是沒(méi)有 ScrollBar 的,必須使用 ScrollBar 組件。ScrollBar 組件通過(guò)監(jiān)聽(tīng) ScrollView 的 ScrollNotification 來(lái)刷新位置,所以 List 的長(zhǎng)度必須是固定的。

當(dāng)使用 WebView 等高度不定的組件時(shí)會(huì)出現(xiàn)內(nèi)容被截?cái)嗟那闆r,通??梢允褂?NestedScrollView 來(lái)解決該問(wèn)題,需要在 WebView 外部嵌套 SingleChildScrollView。

雖然使用了緩存,而且也是用 builder 加載圖片的,但是發(fā)現(xiàn)一個(gè)現(xiàn)象:滑動(dòng)屏幕后圖片短暫消失并重新加載了。圖片高度很高時(shí)這種現(xiàn)象更加明顯,其原因是超出屏幕范圍一定距離的組件被重新渲染了。解決方法是在 ListView 上設(shè)置 cacheExtent 參數(shù):

該參數(shù)的作用是改變超出屏幕高度后繼續(xù)渲染的范圍(以像素為單位),比如設(shè)置成 9999 后意味著超出屏幕 10000 像素以內(nèi)的內(nèi)容都會(huì)被保留下來(lái)。

借助 IntrinsicHeight 組件:

另外,IntrinsicHeight 還可以用于 Dialog 或者 BottomSheet 中,使得其中的元素 顯示內(nèi)在元素的高度 ,從而避免元素因?yàn)榧s束的存在而不顯示或者高度太高(比如在使用了 Column 或者 Row 的時(shí)候)。

在通過(guò) Uri 的 queryParameters 獲取 query 參數(shù)時(shí),發(fā)現(xiàn)有些鏈接會(huì)拋出下面異常:

造成該異常的原因是 Uri 默認(rèn)使用 utf-8 解碼超鏈接字符串,如果鏈接中包含非 utf-8 字符,就會(huì)造成上面的錯(cuò)誤,相關(guān) issue 見(jiàn): issue #31621 。目前該 issue 處于 open 的狀態(tài),暫時(shí)的解決辦法是,在所有使用到 queryParameter 的地方用 try..catch 捕捉可能拋出的異常。

Flutter 開(kāi)發(fā)非常依賴各種官方或第三方的插件,而在使用這些插件時(shí)多少都會(huì)遇到一些問(wèn)題,大部分問(wèn)題都可以通過(guò)搜索和查找 issue 來(lái)解決。這里記錄下一些我在使用部分插件時(shí)遇到的問(wèn)題及其解決方法。

目前該庫(kù)沒(méi)有圖片加載完成的回調(diào)(見(jiàn) issue #545 ),不過(guò)我們可以通過(guò)在 imageBuilder 中來(lái)添加回調(diào):

這是一個(gè)應(yīng)用內(nèi)更新插件,安卓 10 以上安裝時(shí)需要在 manifest 中添加以下內(nèi)容:

目前功能最強(qiáng)大的 WebView 插件,基本能滿足絕大部分移動(dòng)端網(wǎng)頁(yè)加載的需求,而且可定制化程度高。

一般通過(guò) CookieManager 修改 Cookie,攔截請(qǐng)求并修改請(qǐng)求對(duì)象的 Header 不會(huì)生效。

InAppWebViewOptions 的 userAgent 只在 iOS 上生效,而 applicationNameForUserAgent 只在 Android 上生效,所以最好的做法是分平臺(tái)設(shè)置 InAppWebViewOptions ,而且需要注意,由于設(shè)置 userAgent 后會(huì)覆蓋默認(rèn)的 UserAgent,所以如果需要在默認(rèn)的 UserAgent 上添加其它參數(shù),iOS 上需要通過(guò) InAppWebViewController.getDefaultUserAgent() 獲取默認(rèn) UserAgent 參數(shù),而 Android 不需要添加。

如果圖片源或者請(qǐng)求是 http 的,為了在 Android 上正常加載請(qǐng)求,必須在 AndroidInAppWebViewOptions 中將 mixedContentMode 設(shè)置為 AndroidMixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW 。

當(dāng)我們想要設(shè)置全屏圖片的時(shí)候,由于默認(rèn)的 Constraint 會(huì)將圖片居中顯示,所以圖片四周會(huì)留有空隙。為了去除這個(gè)限制,我們需要 Xcode 中打開(kāi) LaunchScreen.storyboard,然后在 View Controller 的 View 和 LaunchImage 上的 Safe Area 去掉。

具體設(shè)置方法:右側(cè) Inspector 面板 Show the Size inspector 解選 Layout Margins 中的 Safe Area Relative Margins,拖動(dòng)圖片占滿全屏,然后根據(jù) View Controller Scene 的 Warning,更新 Constraint 就可以了。

在集成某些三方庫(kù)之后,在使用命令行運(yùn)行 iOS 模擬器的時(shí)候可能會(huì)遇到下面這個(gè)報(bào)錯(cuò):

這是因?yàn)?iOS 模擬器未來(lái)將會(huì)兼容 arm64 架構(gòu),但是目前還不支持,所以我們需要修改 Build Setting 使得能夠在 x86_64 的模擬器上運(yùn)行,操作步驟見(jiàn) 這里 。

Flutter的setState(狀態(tài)刷新)

Flutter有兩個(gè)常用的狀態(tài)類:

標(biāo)記為dirty,執(zhí)行的markNeedsBuild,定義在Element類中:

當(dāng)前Element節(jié)點(diǎn)被標(biāo)記為dirty,同時(shí)調(diào)用owner的scheduleBuildFor方法:

將element元素添加到全局的“臟”鏈表里。

BuildOwner用來(lái)管理哪些需要更新的Widget。這個(gè)owner最開(kāi)始被初始化的地方在WidgetsBinding的initInstances方法中,隨后初始化了onBuildScheduled方法,對(duì)應(yīng)執(zhí)行的是_handleBuildScheduled,定義在WidgetsBinding類中:

ensureVisualUpdate 方法定義在SchedulerBinding類中:

在提交下一幀繪制的時(shí)候會(huì)調(diào)用到scheduleFrame方法,提交給引擎繪制,看看scheduleFrame方法,也定義在SchedulerBinding類中:

提交給引擎繪制之后,會(huì)收到onDrawFrame的回調(diào),最終執(zhí)行到_handleDrawFrame方法中,對(duì)應(yīng)的是handleDrawFrame方法,定義在SchedulerBinding類中:

在RendererBinding的initInstances方法中添加了一個(gè)回調(diào)到這個(gè)List中,對(duì)應(yīng)的是RenderBinding的drawFrame方法,對(duì)應(yīng)的節(jié)點(diǎn)進(jìn)行繪制渲染操作。

WidgetsBinding中的drawFrame方法:

看看這里的buildScope方法,定義在BuildOwner方法中。在上面 scheduleBuildFor 方法介紹中有提到:"scheduleBuildFor 是把一個(gè) element 添加到 _dirtyElements 鏈表,以便當(dāng)[WidgetsBinding.drawFrame]中調(diào)用 buildScope 的時(shí)候能夠重構(gòu) element。onBuildScheduled()是一個(gè) BuildOwner 的回調(diào)"。在 drawFrame 中調(diào)用 buildOwner.buildScope(renderViewElement)更新 elements。

_dirtyElements列表在遍歷的過(guò)程中執(zhí)行rebuild方法,此時(shí)將所有標(biāo)記為dirty的Element節(jié)點(diǎn)依次執(zhí)行rebuild,preformRebuild,build,updateChild,update方法,執(zhí)行界面更新。完成build,update操作完成之后,后續(xù)會(huì)將需要繪制的RenderObject添加到需要layout的列表中,等待繪制渲染。所有繪制完成之后將_dirtyElments列表清空,_inDirtyList標(biāo)記位置為false。

提交給引擎繪制渲染

看看super.drawFrame(),這里就執(zhí)行到了RendererBinding類中,定義如下:

這里就是將最終需要繪制渲染的畫(huà)面提交給引擎的地方了,繪制完成之后就在界面顯示更新后的視圖了。

Flutter 小技巧之優(yōu)化你使用的 BuildContext

Flutter 里的 BuildContext 相信大家都不會(huì)陌生,雖然它叫 Context,但是它實(shí)際是 Element 的抽象對(duì)象,而在 Flutter 里,它主要來(lái)自于 ComponentElement 。

關(guān)于 ComponentElement 可以簡(jiǎn)單介紹一下,在 Flutter 里根據(jù) Element 可以簡(jiǎn)單地被歸納為兩類:

所以一般情況下,我們?cè)? build 方法或者 State 里獲取到的 BuildContext 其實(shí)就是 ComponentElement 。

那使用 BuildContext 有什么需要注意的問(wèn)題 ?

首先如下代碼所示,在該例子里當(dāng)用戶點(diǎn)擊 FloatingActionButton 的時(shí)候,代碼里做了一個(gè) 2秒的延遲,然后才調(diào)用 pop 退出當(dāng)前頁(yè)面。

正常情況下是不會(huì)有什么問(wèn)題,但是當(dāng)用戶在點(diǎn)擊了 FloatingActionButton 之后,又馬上點(diǎn)擊了 AppBar 返回退出應(yīng)用,這時(shí)候就會(huì)出現(xiàn)以下的錯(cuò)誤提示。

可以看到此時(shí) log 說(shuō),Widget 對(duì)應(yīng)的 Element 已經(jīng)不在了,因?yàn)樵? Navigator.of(context) 被調(diào)用時(shí), context 對(duì)應(yīng)的 Element 已經(jīng)隨著我們的退出銷毀。

一般情況下處理這個(gè)問(wèn)題也很簡(jiǎn)單, 那就是增加 mounted 判斷,通過(guò) mounted 判斷就可以避免上述的錯(cuò)誤 。

上面代碼里的 mounted 標(biāo)識(shí)位來(lái)自于 State , 因?yàn)? State 是依附于 Element 創(chuàng)建,所以它可以感知 Element 的生命周期 ,例如 mounted 就是判斷 _element != null; 。

那么到這里我們收獲了一個(gè)小技巧: 使用 BuildContext 時(shí),在必須時(shí)我們需要通過(guò) mounted 來(lái)保證它的有效性 。

那么單純使用 mounted 就可以滿足 context 優(yōu)化的要求了嗎 ?

如下代碼所示,在這個(gè)例子里:

由于在 5 秒之內(nèi),Item 被劃出了屏幕,所以對(duì)應(yīng)的 Elment 其實(shí)是被釋放了,從而由于 mounted 判斷, SnackBar 不會(huì)被彈出。

那如果假設(shè)需要在開(kāi)發(fā)時(shí)展示點(diǎn)擊數(shù)據(jù)上報(bào)的結(jié)果,也就是 Item 被釋放了還需要彈出,這時(shí)候需要如何處理 ?

我們知道不管是 ScaffoldMessenger.of(context) 還是 Navigator.of(context) ,它本質(zhì)還是通過(guò) context 去往上查找對(duì)應(yīng)的 InheritedWidget 泛型,所以其實(shí)我們可以提前獲取。

所以,如下代碼所示,在 Future.delayed 之前我們就通過(guò) ScaffoldMessenger.of(context); 獲取到 sm 對(duì)象,之后就算你直接退出當(dāng)前的列表頁(yè)面,5秒過(guò)后 SnackBar 也能正常彈出。

為什么頁(yè)面銷毀了,但是 SnackBar 還能正常彈出 ?

因?yàn)榇藭r(shí)通過(guò) of(context); 獲取到的 ScaffoldMessenger 是存在 MaterialApp 里,所以就算頁(yè)面銷毀了也不影響 SnackBar 的執(zhí)行。

但是如果我們修改例子,如下代碼所示,在 Scaffold 上面多嵌套一個(gè) ScaffoldMessenger ,這時(shí)候在 Item 里通過(guò) ScaffoldMessenger.of(context) 獲取到的就會(huì)是當(dāng)前頁(yè)面下的 ScaffoldMessenger 。

這種情況下我們只能保證Item 不可見(jiàn)的時(shí)候 SnackBar 還能正常彈出, 而如果這時(shí)候我們直接退出頁(yè)面,還是會(huì)出現(xiàn)以下的錯(cuò)誤提示,因?yàn)? ScaffoldMessenger 也被銷毀了 。

所以到這里我們收獲第二個(gè)小技巧: 在異步操作里使用 of(context) ,可以提前獲取,之后再做異步操作,這樣可以盡量保證流程可以完整執(zhí)行 。

既然我們說(shuō)到通過(guò) of(context) 去獲取上層共享往下共享的 InheritedWidget ,那在哪里獲取就比較好 ?

還記得前面的 log 嗎?在第一個(gè)例子出錯(cuò)時(shí),log 里就提示了一個(gè)方法,也就是 State 的 didChangeDependencies 方法。

為什么是官方會(huì)建議在這個(gè)方法里去調(diào)用 of(context) ?

首先前面我們一直說(shuō),通過(guò) of(context) 獲取到的是 InheritedWidget ,而 當(dāng) InheritedWidget 發(fā)生改變時(shí),就是通過(guò)觸發(fā)綁定過(guò)的 Element 里 State 的 didChangeDependencies 來(lái)觸發(fā)更新, 所以在 didChangeDependencies 里調(diào)用 of(context) 有較好的因果關(guān)系 。

那我能在 initState 里提前調(diào)用嗎 ?

當(dāng)然不行,首先如果在 initState 直接調(diào)用如 ScaffoldMessenger.of(context).showSnackBar 方法,就會(huì)看到以下的錯(cuò)誤提示。

這是因?yàn)?Element 里會(huì)判斷此時(shí)的 _StateLifecycle 狀態(tài),如果此時(shí)是 _StateLifecycle.created 或者 _StateLifecycle.defunct ,也就是在 initState 和 dispose ,是不允許執(zhí)行 of(context) 操作。

當(dāng)然,如果你硬是想在 initState 下調(diào)用也行,增加一個(gè) Future 執(zhí)行就可以成功執(zhí)行

那我在 build 里直接調(diào)用不行嗎 ?

直接在 build 里調(diào)用肯定可以,雖然 build 會(huì)被比較頻繁執(zhí)行,但是 of(context) 操作其實(shí)就是在一個(gè) map 里通過(guò) key - value 獲取泛型對(duì)象,所以對(duì)性能不會(huì)有太大的影響。

真正對(duì)性能有影響的是 of(context) 的綁定數(shù)量和獲取到對(duì)象之后的自定義邏輯 ,例如你通過(guò) MediaQuery.of(context).size 獲取到屏幕大小之后,通過(guò)一系列復(fù)雜計(jì)算來(lái)定位你的控件。

例如上面這段代碼,可能會(huì)導(dǎo)致鍵盤(pán)在彈出的時(shí)候,雖然當(dāng)前頁(yè)面并沒(méi)有完全展示,但是也會(huì)導(dǎo)致你的控件不斷重新計(jì)算從而出現(xiàn)卡頓。

所以到這里我們又收獲了一個(gè)小技巧: 對(duì)于 of(context) 的相關(guān)操作邏輯,可以盡量放到 didChangeDependencies 里去處理 。

Flutter開(kāi)發(fā)性能提升之:如何避免Widget重復(fù)Build

問(wèn)題描述:

在Flutter開(kāi)發(fā)的過(guò)程中,當(dāng)我們獲取到新的數(shù)據(jù)或者數(shù)據(jù)發(fā)生變化,需要去執(zhí)行setState進(jìn)行頁(yè)面刷新的時(shí)候,經(jīng)常會(huì)出現(xiàn)不必要的子節(jié)點(diǎn)Widget也進(jìn)行了build,但實(shí)際上我們是不想讓它再次build,出現(xiàn)這些問(wèn)題的典型情況是在使用FutureBuilder的時(shí)候,例如:

在上面這個(gè)示例中,如果再次調(diào)用Build方法,則會(huì)觸發(fā)httpCall()的方法。

那么怎樣才能避免不必要的部件構(gòu)建呢?

分析:

在Flutter中,Build方法的設(shè)計(jì)方式是pure/without side effects,書(shū)面意思是無(wú)副作用的/純粹的,簡(jiǎn)單點(diǎn)理解我們可以將其含義看作不會(huì)對(duì)外部的方法或者變量產(chǎn)生影響的。這是因?yàn)樵S多外部因素能夠觸發(fā)新的小部件的構(gòu)建,例如這些情況:

但是,這也意味著B(niǎo)uild方法可以不去觸發(fā)httpCall()的方法或者不修改任何狀態(tài)。

解決

回歸問(wèn)題,當(dāng)前我們面臨的問(wèn)題是Build方法造成了副作用,也就是造成了無(wú)關(guān)的Build調(diào)用麻煩。

所以,只要我們使Build方法保持純粹/無(wú)副作用,這樣就算多少次調(diào)用它,也不會(huì)對(duì)其他Widget的Build方法產(chǎn)生影響。

在上面的示例中,我們將Widget轉(zhuǎn)換為StatefulWidget,然后提取httpCall()到initState中,這樣問(wèn)題就解決了

另外,還可以使一個(gè)Widget能夠在不強(qiáng)迫其子部件也構(gòu)建的情況下進(jìn)行重新構(gòu)建。

在Widget的實(shí)例保持不變時(shí);Flutter會(huì)有意識(shí)的不去重建子部件。這意味著我們可以緩存Widget樹(shù)的某些部分,以防止不必要的重新構(gòu)建。

最簡(jiǎn)單的方法是使用const修飾構(gòu)造函數(shù):

由于const的修飾,即使調(diào)用了數(shù)百次build,DecoratedBox的實(shí)例也將保持不變。

或者你可以這樣使用以達(dá)到相同的結(jié)果:

在這個(gè)例子中,當(dāng)StreamBuilder收到新值的通知時(shí),即使StreamBuilder的Column進(jìn)行了重構(gòu),subtree也不會(huì)進(jìn)行重構(gòu)。這是因?yàn)橛捎陂]包,MyWidget的實(shí)例沒(méi)有改變。

這種模式在動(dòng)畫(huà)中經(jīng)常使用。典型的是使用AnimatedBuilder和所有的*Transition時(shí),例如AlignTransition。

我們還可以將subtree存儲(chǔ)到類的一個(gè)字段中,但是并不推薦你這樣做,因?yàn)樗鼤?huì)破壞Flutter的熱重載。

Flutter項(xiàng)目遇到的問(wèn)題及解決方法記錄

問(wèn)題原因:版本大小原因

解決方案:點(diǎn)擊ios文件夾-點(diǎn)擊podfile修改如下(親測(cè)實(shí)用已解決)

解決方案:

刪除IOS工程中的Podfile.lock文件,重新pod install(繼續(xù)執(zhí)行Flutter run操作)即可解決。

問(wèn)題原因:沒(méi)有配置safety相關(guān)

解決方案:可以直接在命令行中執(zhí)行:

然后在Additional run args 這行鍵入一下命令即可。

問(wèn)題原因:xcode版本更新導(dǎo)致的

解決方案:將爆紅處一行隱藏,運(yùn)行,點(diǎn)擊fix即可,會(huì)顯示正確位置就解決了。

解決方法:

1、找到第一個(gè)爆紅文件,將311行問(wèn)號(hào)后面的Colors.white刪除即可

2、 在相同的文件夾內(nèi)找到第二個(gè)爆紅文件,找到106行,刪除super.addAllowedPoint這一行即可。

造成原因分析:還不清楚

群友幫助:經(jīng)常出現(xiàn),重啟電腦多試試

解決方案:暫未找到

分享文章:flutter操作技巧,簡(jiǎn)書(shū) flutter
URL網(wǎng)址:http://muchs.cn/article32/phjspc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁(yè)設(shè)計(jì)公司微信公眾號(hào)、關(guān)鍵詞優(yōu)化、面包屑導(dǎo)航、標(biāo)簽優(yōu)化、電子商務(wù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站