Saltar al contenido principal

Manejo de errores en Flutter

Cómo controlar los mensajes de error y el registro de errores

El framework de Flutter captura los errores que ocurren durante los callbacks activados por el propio framework, incluyendo los errores encontrados durante las fases de build, layout y paint. Los errores que no ocurren dentro de los callbacks de Flutter no pueden ser capturados por el framework, pero puedes manejarlos configurando un manejador de errores en el PlatformDispatcher.

Todos los errores capturados por Flutter se dirigen al manejador FlutterError.onError. Por defecto, este llama a FlutterError.presentError, el cual vuelca el error en los logs del dispositivo. Al ejecutar desde un IDE, el inspector invalida este comportamiento para que los errores también se puedan dirigir a la consola del IDE, lo que te permite inspeccionar los objetos mencionados en el mensaje.

Cuando ocurre un error durante la fase de build, se invoca el callback ErrorWidget.builder para construir el Widget que se utiliza en lugar del que falló. Por defecto, en modo debug esto muestra un mensaje de error en rojo, y en modo release muestra un fondo gris.

Cuando ocurren errores sin un callback de Flutter en la pila de llamadas, son manejados por el callback de error del PlatformDispatcher. Por defecto, este solo imprime los errores y no hace nada más.

Puedes personalizar estos comportamientos, típicamente asignándoles valores en tu función void main().

A continuación se explica el manejo de cada tipo de error. Al final hay un fragmento de código que maneja todo tipo de errores. Aunque puedes simplemente copiar y pegar el fragmento, te recomendamos que primero te familiarices con cada uno de los tipos de error.

Errores capturados por Flutter

#

Por ejemplo, para hacer que tu aplicación se cierre inmediatamente cada vez que Flutter capture un error en modo release, podrías usar el siguiente manejador:

dart
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());
}

// The rest of the `flutter create` code...

Este manejador también se puede utilizar para reportar errores a un servicio de registro de logs. Para obtener más detalles, consulta nuestro capítulo del cookbook para reportar errores a un servicio.

Definir un Widget de error personalizado para los errores de la fase de build

#

Para definir un Widget de error personalizado que se muestre cada vez que el builder no pueda construir un Widget, utiliza MaterialApp.builder.

dart
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');
      },
    );
  }
}

Errores no capturados por Flutter

#

Considera un callback onPressed que invoca una función asíncrona, como MethodChannel.invokeMethod (o prácticamente cualquier plugin). Por ejemplo:

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

Si invokeMethod lanza un error, no se reenviará a FlutterError.onError. En su lugar, se reenvía al PlatformDispatcher.

Para capturar dicho error, utiliza PlatformDispatcher.instance.onError.

dart
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());
}

Manejar todo tipo de errores

#

Supongamos que quieres salir de la aplicación ante cualquier excepción y mostrar un Widget de error personalizado cada vez que falle la construcción de un Widget; puedes basar tu manejo de errores en el siguiente fragmento de código:

dart
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');
      },
    );
  }
}