Что выбрасывает программа при краше (crash) в Dart?

Ответ

При необработанном исключении в Dart выбрасывается объект, унаследованный от класса Error или Exception, и выполнение потока прерывается.

Основные типы ошибок, которые я чаще всего вижу в логах:

  • RangeError (подкласс Error): Выход за границы коллекции.
    var list = [1, 2];
    print(list[5]); // RangeError (index): Invalid value: Not in range 0..1, inclusive
  • NoSuchMethodError (подкласс Error): Вызов метода или геттера у null или несуществующего члена.
    String? nullableString;
    print(nullableString.length); // NoSuchMethodError: The getter 'length' was called on null.
  • StateError: Нарушение инварианта объекта (например, вызов add на закрытом EventSink).

Обработка: Для глобального перехвата таких ошибок в Flutter-приложении я настраиваю:

void main() {
  FlutterError.onError = (details) {
    // Отправляем в Crashlytics/Sentry
    myCrashReportingTool.recordError(details.exception, details.stack);
  };
  PlatformDispatcher.instance.onError = (error, stack) {
    // Перехват ошибок вне контекста Flutter
    myCrashReportingTool.recordError(error, stack);
    return true; // Предотвращаем завершение изолятора
  };
  runApp(const MyApp());
}

Это позволяет логировать краши и, по возможности, gracefully завершать работу приложения.

Ответ 18+ 🔞

А, ну это же классика, ёпта! Смотри, когда в Dart'е какая-то хрень случается — вылетает необработанное исключение, — то летит объект, который наследуется либо от Error, либо от Exception. И всё, поток выполнения накрывается медным тазом, привет.

Вот какие косяки я чаще всего в логах наблюдаю, ядрёна вошь:

  • RangeError (это подкласс Error): Это когда кто-то полез куда не надо, за границы коллекции. Типичный распиздяй.

    var list = [1, 2];
    print(list[5]); // RangeError (index): Invalid value: Not in range 0..1, inclusive

    Ну и что он хотел-то, с пятого элемента? Его же нихуя нет!

  • NoSuchMethodError (тоже подкласс Error): Это святое. Вызов метода или геттера у null. Любимая ошибка всех, кто про ? забывает.

    String? nullableString;
    print(nullableString.length); // NoSuchMethodError: The getter 'length' was called on null.

    Ну да, конечно, у null есть свойство length, ага, щас. Доверия ебать ноль к такому коду.

  • StateError: Это когда объект в таком состоянии, что с ним уже нельзя вот эту конкретную операцию делать. Например, пытаться что-то добавить в уже закрытый EventSink. Сам от себя охуеешь, когда такое в продакшене увидишь.

Обработка: Чтобы эти все летящие во все дыры ошибки не добили приложение окончательно, в Flutter'е я обычно настраиваю глобальные перехватчики. Э сабака сука, без них никуда.

void main() {
  FlutterError.onError = (details) {
    // Отправляем в Crashlytics/Sentry
    myCrashReportingTool.recordError(details.exception, details.stack);
  };
  PlatformDispatcher.instance.onError = (error, stack) {
    // Перехват ошибок вне контекста Flutter
    myCrashReportingTool.recordError(error, stack);
    return true; // Предотвращаем завершение изолятора
  };
  runApp(const MyApp());
}

Так хоть какая-то польза: краши логируются, и можно попытаться приложение не аварийно закрыть, а как-то по-человечески. А то волнение ебать каждый раз, когда юзер пишет «ваше приложение закрылось».