Flutter中灰屏問(wèn)題的原因及解決方法
生產(chǎn)中的 flutter 應(yīng)用程序中的灰屏是一種通用占位符,當(dāng)框架遇到問(wèn)題無(wú)法渲染預(yù)期用戶界面時(shí)就會(huì)顯示。是的,所以基本上是出現(xiàn)問(wèn)題時(shí)的后備指示器。
有趣的是,這只出現(xiàn)在發(fā)布模式下。在任何其他模式下運(yùn)行都會(huì)顯示紅色錯(cuò)誤屏幕,并說(shuō)明導(dǎo)致錯(cuò)誤的原因。 (檢查此處以了解各種類型的構(gòu)建模式。)此類錯(cuò)誤的常見原因是:
- 未處理的異常:這些是運(yùn)行時(shí)發(fā)生的錯(cuò)誤,未使用
try-catch
塊捕獲。 - 渲染錯(cuò)誤:這些是渲染布局時(shí)引起的問(wèn)題,例如,在
Column
、Row
或Flex
小部件外部使用Expanded
時(shí)引起的問(wèn)題。
以下是可能導(dǎo)致灰屏的代碼示例:
class HomeView extends HookWidget { const HomeView({super.key}); @override Widget build(BuildContext context) { const widget = null; return Scaffold( appBar: AppBar( title: Text( 'Gallery', style: Theme.of(context).textTheme.headlineLarge, ), ), body: widget!, ); } }
在這里,我們犯了一個(gè)明顯的錯(cuò)誤,在我們知道的 null
小部件上使用了 bang 運(yùn)算符(!),這導(dǎo)致在非發(fā)布模式下出現(xiàn)紅屏,在發(fā)布模式下出現(xiàn)灰屏。
需要注意的是,我們不建議在不更新的情況下將部件明確設(shè)置為空值,空值錯(cuò)誤是一個(gè)常見錯(cuò)誤,而上述操作是重現(xiàn)該錯(cuò)誤的簡(jiǎn)單方法。
調(diào)試模式下的紅色錯(cuò)誤屏幕(左)和發(fā)布模式下的灰色屏幕(右)的圖像
自定義錯(cuò)誤屏幕
為了顯示更用戶友好的消息而不是灰屏,我們將策略性地在 main
函數(shù)中放置一行代碼。該行充當(dāng)預(yù)防措施,確保每當(dāng)發(fā)生未處理的異常時(shí)都會(huì)顯示自定義錯(cuò)誤屏幕。
void main() { ErrorWidget.builder = (_) => const AppErrorWidget(); // This line does the magic! runApp(MyApp()); }
有條件的紅屏(可選):
也許您希望在開發(fā)過(guò)程中看到默認(rèn)的紅色錯(cuò)誤屏幕以進(jìn)行調(diào)試。您可以通過(guò)將 ErrorWidget.builder
賦值包裝在檢查當(dāng)前構(gòu)建模式的 if
語(yǔ)句中來(lái)實(shí)現(xiàn)此目的:
void main() { if (kReleaseMode) ErrorWidget.builder = (_) => const AppErrorWidget(); runApp(MyApp()); }
下一步涉及創(chuàng)建 AppErrorWidget
本身的內(nèi)容。該小部件將確定發(fā)生未處理的異常時(shí)用戶看到的內(nèi)容。
class AppErrorWidget extends StatelessWidget { const AppErrorWidget({super.key}); @override Widget build(BuildContext context) { return const Material( color: Colors.white, child: Padding( padding: EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.warning, size: 200, color: Colors.amber, ), SizedBox(height: 48), Text( 'So... something funny happened', textAlign: TextAlign.center, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), SizedBox(height: 16), Text( 'This error is crazy large it covers your whole screen. But no worries' ' though, we\'re working to fix it.', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, ), ), ], ), ), ); } }
雖然鼓勵(lì)自定義應(yīng)用程序的體驗(yàn),但 ErrorWidget.builder
上的 Flutter 文檔提醒我們,調(diào)用錯(cuò)誤小部件時(shí)視圖處于不穩(wěn)定狀態(tài)。構(gòu)建(可能還有布局)期間的異常會(huì)使系統(tǒng)處于脆弱狀態(tài)。為了最大限度地減少進(jìn)一步的問(wèn)題,返回的小部件應(yīng)該做最少的工作。 LeafRenderObjectWidget
(如默認(rèn)的 RenderErrorBox
)非常適合處理意外約束。
ErrorWidget.builder 的幕后花絮
現(xiàn)在我們知道,當(dāng)渲染預(yù)期 UI 的過(guò)程中發(fā)生錯(cuò)誤時(shí), ErrorWidget.builder
就會(huì)被調(diào)用,但是這到底是如何實(shí)現(xiàn)的呢?
如果我們深入研究 Flutter 的框架,我們會(huì)在構(gòu)建或重建小部件時(shí)看到一個(gè)名為 _updateChild()
的方法。
void _updateChild() { try { final Widget child = (widget as _RawView).builder(this, _effectivePipelineOwner); _child = updateChild(_child, child, null); } catch (e, stack) { final FlutterErrorDetails details = FlutterErrorDetails( exception: e, stack: stack, library: 'widgets library', context: ErrorDescription('building $this'), informationCollector: !kDebugMode ? null : () => <DiagnosticsNode>[ DiagnosticsDebugCreator(DebugCreator(this)), ], ); FlutterError.reportError(details); final Widget error = ErrorWidget.builder(details); _child = updateChild(null, error, slot); } }
我們可以看到 ErrorWidget.builder
屬性用于根據(jù)提供的 FlutterErrorDetails
檢索自定義錯(cuò)誤小部件;然后更新 _child
變量以顯示自定義錯(cuò)誤小部件而不是原始子小部件。
提升開發(fā)者體驗(yàn)
定制向用戶呈現(xiàn)錯(cuò)誤的方式是改善用戶體驗(yàn)的關(guān)鍵一步。雖然 ErrorWidget.builder
幫助我們?cè)诔霈F(xiàn)錯(cuò)誤時(shí)管理用戶體驗(yàn),但它并沒有為生產(chǎn)環(huán)境中的開發(fā)人員提供有價(jià)值的見解。本地調(diào)試不再是一種選擇,那么我們?nèi)绾渭皶r(shí)了解用戶設(shè)備上發(fā)生的錯(cuò)誤呢?
這就是我們利用 FlutterError.onError
回調(diào)的力量的地方。讓我們看看這是如何完成的:
void main() { if (kReleaseMode) ErrorWidget.builder = (_) => const AppErrorWidget(); FlutterError.onError = (details) { FlutterError.dumpErrorToConsole(details); if (!kReleaseMode) return; // 發(fā)送到您的 crashlytics 服務(wù)... }; runApp(MyApp()); }
我們添加了一行新代碼,它將新的回調(diào)函數(shù)分配給 FlutterError.onError
屬性。每當(dāng)使用 FlutterError.reportError
報(bào)告錯(cuò)誤時(shí)都會(huì)調(diào)用此回調(diào)。
在回調(diào)內(nèi)部, FlutterError.dumpErrorToConsole(details)
通過(guò)將錯(cuò)誤詳細(xì)信息轉(zhuǎn)儲(chǔ)到控制臺(tái)來(lái)幫助我們了解幕后情況。這對(duì)于在部署或分階段部署期間可能存在對(duì)用戶設(shè)備的訪問(wèn)受限的調(diào)試目的非常有用。
最后的注釋行 ( // 發(fā)送到您的 crashlytics 服務(wù)...
) 強(qiáng)調(diào)了這種方法的真正威力。在這里,您可以集成您選擇的錯(cuò)誤報(bào)告服務(wù)(例如 Crashlytics)以發(fā)送詳細(xì)的錯(cuò)誤報(bào)告以供分析。
注意:此行包含在
if
語(yǔ)句中,以確保它僅在調(diào)試或分析模式下執(zhí)行 (!kReleaseMode
)。
避免灰屏的最佳錯(cuò)誤處理實(shí)踐
我們已經(jīng)了解了導(dǎo)致灰屏的原因以及出現(xiàn)灰屏?xí)r如何更好地處理它;我們還應(yīng)該介紹的一件事是,作為開發(fā)人員可以采取哪些措施來(lái)避免出現(xiàn)灰屏。其中一些是:
- 擁抱 try-catch :將關(guān)鍵代碼部分包裝在
try-catch
塊內(nèi)。這允許您捕獲潛在的異常并提供優(yōu)雅的回退機(jī)制。 - 少用 Bang 運(yùn)算符 (!):bang 運(yùn)算符 (
!
) 是 null 斷言檢查的快捷方式,但如果用于不確定是否為非 null 的值,可能會(huì)導(dǎo)致意外錯(cuò)誤。更多地使用條件表達(dá)式 (??
) 或 null 感知訪問(wèn)運(yùn)算符 (?.
)。 - 徹底的應(yīng)用程序測(cè)試:結(jié)合使用單元、小部件、集成和手動(dòng)測(cè)試來(lái)幫助在問(wèn)題出現(xiàn)在生產(chǎn)中之前識(shí)別和解決問(wèn)題。
- 尊重 Widget 約束:Flutter 中的每個(gè) Widget 都有局限性和預(yù)期的使用模式;避免在其限制之外使用它們,例如在可滾動(dòng)視圖中使用
Spacer
。
結(jié)論:擁抱不可避免的事情
錯(cuò)誤處理是任何編寫良好的 Flutter 應(yīng)用程序的重要組成部分。當(dāng)您努力編寫干凈的代碼并預(yù)測(cè)潛在問(wèn)題時(shí),異常情況必然會(huì)發(fā)生。通過(guò)實(shí)施 ErrorWidget.builder
,您可以確保即使發(fā)生意外情況,您的用戶也會(huì)看到清晰且內(nèi)容豐富的消息,而不是令人困惑的灰屏,并且通過(guò) FlutterError.onError
您可以確保您記錄這些意外錯(cuò)誤,并且可以更輕松地調(diào)試和修復(fù)這些錯(cuò)誤。
以上就是Flutter中灰屏問(wèn)題的原因及解決方法的詳細(xì)內(nèi)容,更多關(guān)于Flutter中灰屏問(wèn)題的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拖動(dòng)效果的兩種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04Android startActivityForResult和setResult的區(qū)別
這篇文章主要介紹了 Android startActivityForResult和setResult的區(qū)別的相關(guān)資料,希望通過(guò)本文能幫助大家理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08Android中的Notification機(jī)制深入理解
這篇文章主要給大家介紹了關(guān)于Android中Notification機(jī)制的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02Android 7.0 運(yùn)行時(shí)權(quán)限彈窗問(wèn)題的解決
這篇文章主要介紹了Android 7.0 運(yùn)行時(shí)權(quán)限彈窗問(wèn)題的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03如何為RecyclerView添加Header和Footer
這篇文章主要為大家詳細(xì)介紹了如何為RecyclerView添加Header和Footer,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android ViewFlipper翻轉(zhuǎn)視圖使用詳解
這篇文章主要為大家詳細(xì)介紹了Android ViewFlipper翻轉(zhuǎn)視圖的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Android實(shí)現(xiàn)可折疊式標(biāo)題欄
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)可折疊式標(biāo)題欄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09Android自定義view繪制圓環(huán)占比動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android自定義view繪制圓環(huán)占比動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android開發(fā)Dart?Constructors構(gòu)造函數(shù)使用技巧整理
這篇文章主要為大家介紹了Android開發(fā)Dart?Constructors構(gòu)造函數(shù)使用技巧整理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05