errflow

Pub Version Dart CI

A Dart/Flutter package for making it somewhat easier to comprehend the flow of errors and handle them.

Usage

Initialisation and clean-up

Instantiate ErrFlow, with the default error type representing that there is no error.

Make sure to call dispose() when the object of ErrFlow is no longer needed.

enum ErrorTypes {
  none,
  foo,
  bar,
}

...

final errFlow = ErrFlow<ErrorTypes>(ErrorTypes.none);

...

errFlow.dispose();

Setting/logging an error

Use set() to set a custom error type equivalent to the actual exception/error occurring when some process of yours has failed. The listener is notified of the error type and stores it as the last error type (lastError) so that it can be checked later.

The listener also calls the logger to log a set of information about an exception/error if it is provided via set() or log().

Future<bool> yourMethod() {
  try {
    return errorProneProcess();
  } catch(e, s) {
    // This updates the last error type and also triggers logging.
    errFlow.set(ErrorTypes.foo, e, s, 'additional info');

    // Provide only the error type if logging is unnecessary.
    errFlow.set(ErrorTypes.foo);

    // Use log() instead, if you consider the exception as
    // non-problematic and want to just log it.
    errFlow.log(e, s, 'additional info');
  }

  return false;
}

Note that nothing will be logged unless the value of an exception/error is provided, even if the stack trace and the context are given.

Handling errors

scope() executes a function, and handles errors occurring in there according to the conditions specified by errorIf and criticalIf. Use both or either of them to set the conditions of whether to treat the result of the function as non-critical/critical errors.

If either of the conditions is met, the relevant handler (onError or onCriticalError) is called with the function result and the error type passed in. Do some error handling in these handlers, like showing the error to the user.

final result = await errFlow.scope<bool>(
  () => yourMethod(),
  errorIf: (result, errorType) => errorType == ErrorTypes.foo,
  criticalIf: (result, errorType) => errorType == ErrorTypes.bar,
  onError: (result, errorType) => _onError(result, errorType),
  onCriticalError: (result, errorType) => _onCriticalError(result, errorType),
);

The handler functions receive the result and the error type, which means you can combine them to customise the conditions for your preference.

e.g. To make the onError handler called when the process fails for reasons other than a connection error:

errorIf: (result, errorType) => !result && errorType != ErrorTypes.connection

Default error handlers

You may want to consistently use a particular handler for non-critical errors, and the same or another one for critical errors. In such a case, errorHandler and criticalErrorHandler will come in handy. You can specify in advance how errors should be handled, and omit onError and onCriticalError in scope().

void _errorHandler<T>(T result, ErrorTypes type) {
  if (type == ErrorTypes.foo) {
    // Handle the foo error
  } else {
    // Handle other errors
  }
}

...

errFlow
  ..errorHandler = _errorhandler
  ..criticalErrorHandler = _errorHandler;

final result = await errFlow.scope<bool>(
  () => yourMethod(),
  errorIf: (result, errorType) => !result,
);

Logger

To use the default logger, which simply prints information to the console, call useDefaultLogger() before the first logging.

errFlow.useDefaultLogger();

If it is too simple and lacks functionality you need, set your own logger.

void _logger(dynamic e, StackTrace s, {dynamic context}) {
  if (type == ErrorTypes.foo || type == ErrorTypes.bar) {
    Crashlytics.instance.recordError(e, s, context: context);
  } else {
    print('Error: $e');
  }
}

...

errFlow.logger = _logger;

Set the default or a custom logger, otherwise an assertion error will occur in the debug mode.

Adding/removing a listener

This is usually unnecessary, but you can add a custom listener for your special needs.

void _listener({ErrorTypes type, dynamic exception, StackTrace stack, dynamic context}) {
  // Some processing
}

...

errFlow.addListener(_listener);

...

errFlow.removeListener(_listener);

Libraries

errflow
info