5. Mastering Error Handling and Exceptions in Dart
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 FormatExceptionRangeError: Thrown when a value falls outside the valid range.var list = [1, 2, 3]; print(list[5]); // Throws RangeErrorArgumentError: 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
Catch Specific Exceptions:
- Use the
onkeyword to handle known exception types for precise error handling.
- Use the
Avoid Catching All Exceptions:
- Catching all exceptions without rethrowing can mask bugs.
Use
finallyfor Cleanup:- Always clean up resources like files or network connections in the
finallyblock.
- Always clean up resources like files or network connections in the
Log Errors:
- Log exceptions for debugging and monitoring purposes.
print("Error: $e");
Handle Errors in Asynchronous Code:
- Always handle errors in
FutureandStreamoperations using.catchError()ortry-catch.
- Always handle errors in
Create Meaningful Custom Exceptions:
- Custom exceptions should provide clear and helpful error messages.