Skip to main content

Command Palette

Search for a command to run...

5. Mastering Error Handling and Exceptions in Dart

Updated
4 min read

1. Understanding Exceptions in Dart

In Dart, exceptions represent runtime errors that disrupt the normal flow of execution. Rather than crashing the program, exceptions can be caught and managed, allowing developers to address issues gracefully.

Common Built-in Exceptions

Dart provides several built-in exception classes:

  • FormatException: Thrown when a value doesn’t match an expected format.

      int.parse("invalid"); // Throws FormatException
    
  • RangeError: Thrown when a value falls outside the valid range.

      var list = [1, 2, 3];
      print(list[5]); // Throws RangeError
    
  • ArgumentError: Thrown when an invalid argument is passed to a function.

      assert(age > 0, 'Age must be positive'); // Throws ArgumentError
    

2. Handling Exceptions

Using try-catch Blocks

The try-catch block is the most common way to handle exceptions. It allows you to execute code that may throw an exception and provides a mechanism to catch and handle it.

Syntax:

try {
  // Code that might throw an exception
} catch (e) {
  // Handle the exception
}

Example:

void main() {
  try {
    int.parse("notANumber");
  } catch (e) {
    print("An error occurred: $e");
  }
}

Catching Specific Exceptions with on

When you want to handle specific types of exceptions differently, you can use the on keyword.

Syntax:

try {
  // Code that might throw an exception
} on SpecificExceptionType {
  // Handle the specific exception
} catch (e) {
  // Handle all other exceptions
}

Example:

void main() {
  try {
    int.parse("invalid");
  } on FormatException {
    print("Caught a FormatException!");
  } catch (e) {
    print("Caught an unknown exception: $e");
  }
}

Using the finally Block

The finally block ensures that code executes regardless of whether an exception was thrown or caught. It is typically used for cleanup operations.

Syntax:

try {
  // Code that might throw an exception
} catch (e) {
  // Handle the exception
} finally {
  // Code that always executes
}

Example:

void main() {
  try {
    int.parse("invalid");
  } catch (e) {
    print("Caught an exception: $e");
  } finally {
    print("Execution completed.");
  }
}

3. Throwing Exceptions

Dart allows you to throw exceptions explicitly using the throw keyword. You can use built-in exception classes or create custom ones.

Throwing Exceptions:

void validateAge(int age) {
  if (age < 0) {
    throw ArgumentError("Age cannot be negative.");
  }
}

Custom Exceptions

You can define custom exceptions by extending the Exception class.

Example:

class InvalidAgeException implements Exception {
  final String message;
  InvalidAgeException(this.message);
  @override
  String toString() => "InvalidAgeException: $message";
}

void main() {
  try {
    throw InvalidAgeException("Age must be positive.");
  } catch (e) {
    print(e);
  }
}

4. Rethrowing Exceptions

When catching an exception, you may want to rethrow it to be handled at a higher level in the call stack. Dart provides the rethrow keyword for this purpose.

Example:

void validateInput(String input) {
  try {
    int.parse(input);
  } catch (e) {
    print("Logging the error: $e");
    rethrow; // Propagates the exception
  }
}

void main() {
  try {
    validateInput("invalid");
  } catch (e) {
    print("Caught in main: $e");
  }
}

5. Error Handling in Asynchronous Code

In asynchronous programming, errors are handled using Future and Stream APIs. Dart provides mechanisms to handle errors gracefully in asynchronous operations.

Using catchError

The catchError method is used to handle errors in a Future.

Example:

Future<void> fetchData() async {
  return Future.delayed(Duration(seconds: 2), () => throw Exception("Network error"));
}

void main() {
  fetchData().catchError((e) {
    print("Caught an error: $e");
  });
}

Using try-catch with await

When working with async and await, you can use try-catch blocks for error handling.

Example:

Future<void> fetchData() async {
  throw Exception("Network error");
}

void main() async {
  try {
    await fetchData();
  } catch (e) {
    print("Caught an error: $e");
  }
}

6. Best Practices for Error Handling

  1. Catch Specific Exceptions:

    • Use the on keyword to handle known exception types for precise error handling.
  2. Avoid Catching All Exceptions:

    • Catching all exceptions without rethrowing can mask bugs.
  3. Use finally for Cleanup:

    • Always clean up resources like files or network connections in the finally block.
  4. Log Errors:

    • Log exceptions for debugging and monitoring purposes.
    print("Error: $e");
  1. Handle Errors in Asynchronous Code:

    • Always handle errors in Future and Stream operations using .catchError() or try-catch.
  2. Create Meaningful Custom Exceptions:

    • Custom exceptions should provide clear and helpful error messages.

More from this blog

sangama

1285 posts