iT邦幫忙

2024 iThome 鐵人賽

0

Flutter 能夠自動處理在其內部運作過程中出現的錯誤。比如渲染畫面、安排組件位置、建立使用者介面時,如果出現錯誤,Flutter 會自動攔截這些錯誤。但如果錯誤發生在框架管控的範圍之外,例如發送網路請求時或者讀取檔案時出錯,此時 Flutter 就無法自動攔截 catch 這些問題。但我們可以使用 PlatformDispatcher進行錯誤處理。

所有自動攔截到的錯誤都會被導向 FlutterError.onError,預設情況下會呼叫 FlutterError.presentError 方法將錯誤訊息輸出到裝置的日誌中。

但當從 IDE 執行時,情況不太一樣,IDE Inspector 會覆寫這個預設行為,讓錯誤也被導向到編輯器的控制台。如此一來開發者可以更方便的查詢這些錯誤資訊。

當錯誤是在建構時期發生的話會呼叫 ErrorWidget.builder 來建立一個替代性的組件,代替那個出錯的組件。在偵錯模式下,替代組件預設會顯示紅色錯誤訊息,在正式模式(Release mode) 則會顯示一個灰色背景。

其他 Flutter 管控範圍外的錯誤,會在 PlatfromDispatcher 的 error callback 中處理,預設只會輸出錯誤訊息。

我們可以自訂這些行為,一般是在 void main() 裡面設定。 下面我們將介紹各種錯誤類型。

自動攔截的錯誤

若要讓你的應用程式在發布模式下,每當 Flutter 攔截到錯誤時立刻退出,你可以使用以下的處理程序:

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  FlutterError.onError = (details) {
    FlutterError.presentError(details);
    if (kReleaseMode) {
      exit(1);
    }
    runApp(const MyApp());
  }
}

常量 kReleaseMode 用於判斷應用程式是否以發布模式編譯。這個處理程序也可以用來將錯誤回報給日誌記錄服務。

為「建構階段錯誤」自訂錯誤組件

為了自訂錯誤組件用於在 builder 無法建立組件時顯示,可以使用 MaterialApp.builder

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (context, widget) {
        Widget error = const Text('...rendering error...');
        if (widget is Scaffold || widget is Navigator) {
          error = Scaffold(body: Center(child: error));
        }
        ErrorWidget.builder = (errorDetails) => error;
        if (widget != null) return widget;
        throw StateError('widget is null');
      },
    );
  }
}

非自動攔截的錯誤

另一種常見的情況比如所在 onPressed 中呼叫了非同步函式:

OutlinedButton(
  child: const Text('Click me!'),
  onPressed: () async {
    const channel = MethodChannel('crashy-custom-channel');
    await channel.invokeMethod('blah');
  },
)

invokeMethod 拋出例外,此時 FlutterError.onError 無法自動攔截。這個使用需要使用 PlatformDispatcher.instance.onError

import 'package:flutter/material.dart';
import 'dart:ui';

void main() {
  MyBackend myBackend = MyBackend();
  PlatformDispatcher.instance.onError = (error, stack) {
    myBackend.sendError(error, stack);
    return true;
  };
  runApp(const MyApp());
}

處理所有類型的錯誤

假設你希望在任何異常發生時退出應用程式,並且在組件建構失敗時顯示一個自訂的錯誤組件 - 你可以基於以下程式碼片段來處理錯誤:

import 'package:flutter/material.dart';
import 'dart:ui';

Future<void> main() async {
  await myErrorsHandler.initialize();
  FlutterError.onError = (details) {
    FlutterError.presentError(details);
    myErrorsHandler.onErrorDetails(details);
  };
  PlatformDispatcher.instance.onError = (error, stack) {
    myErrorsHandler.onError(error, stack);
    return true;
  };
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (context, widget) {
        Widget error = const Text('...rendering error...');
        if (widget is Scaffold || widget is Navigator) {
          error = Scaffold(body: Center(child: error));
        }
        ErrorWidget.builder = (errorDetails) => error;
        if (widget != null) return widget;
        throw StateError('widget is null');
      },
    );
  }
}

進階資源

若您已經使用了 BLoC 等狀態管理你後續可以參考 How I handle errors in Flutter


上一篇
Rive - 動畫解決方案新選擇
系列文
Flutter 開發實戰 - 30 天逃離新手村34
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言