Exceptions and Error Handling
18.1 Throwing and Catching
double divide(int a, int b) {
if (b == 0) throw ArgumentError('Divisor cannot be zero');
return a / b;
}
void main() {
try {
divide(10, 0);
} on ArgumentError catch (e) { // catch a specific type
print('Bad argument: ${e.message}');
} on FormatException {
print('Bad format');
} catch (e, stackTrace) { // any other object, plus the stack trace
print('Unexpected: $e\n$stackTrace');
rethrow; // propagate while preserving the stack trace
} finally {
print('Always runs');
}
}18.2 Exception vs Error
Expected, recoverable failures (I/O, parsing, network) Programming bugs (shouldn't be caught normally)
FormatException, IOException, HttpException, TimeoutException
ArgumentError, RangeError, StateError, TypeError, UnimplementedError, UnsupportedError, LateInitializationError, ConcurrentModificationError Usually no (fix the bug)
18.3 Custom Exceptions
class InsufficientFundsException implements Exception {
final double shortfall;
const InsufficientFundsException(this.shortfall);
@override
String toString() => 'InsufficientFundsException: short by \$${shortfall.toStringAsFixed(2)}';
}
sealed class AppException implements Exception {
const AppException(this.message);
final String message;
}
final class NetworkException extends AppException { const NetworkException(super.message); }
final class NotFoundException extends AppException { const NotFoundException(super.message); }
void withdraw(double amount, double balance) {
if (amount > balance) throw InsufficientFundsException(amount - balance);
}18.4 Errors Without Exceptions: Result Types
Libraries: fpdart (Either, Option, TaskEither), result_dart, dartz, multiple_result.
sealed class Result<T> {
const Result();
}
final class Ok<T> extends Result<T> { const Ok(this.value); final T value; }
final class Err<T> extends Result<T> { const Err(this.error); final Exception error; }
Result<int> parseAge(String input) {
final n = int.tryParse(input);
if (n == null) return Err(FormatException('Not a number: $input'));
if (n < 0) return Err(FormatException('Age cannot be negative: $n'));
return Ok(n);
}
switch (parseAge('42')) {
case Ok(:final value): print('Age $value');
case Err(:final error): print('Error: $error');
}18.5 Error Handling Best Practices
Throw Exception subtypes for recoverable conditions and Errors for bugs. Use on Type catch for specific handling.
Don't swallow exceptions silently. Use rethrow to propagate. Use Error.throwWithStackTrace to rethrow with an original trace.
Async errors: see Module 25 (uncaught Future errors and zones).