# Error Handling in Dart: A Comprehensive Guide

Proper error handling is crucial for writing robust and reliable Dart applications. This guide covers the main concepts and techniques for handling errors in Dart.

## 1. Try-Catch Blocks

The try-catch block is the primary mechanism for handling exceptions in Dart.

### Example:

```dart
void main() {
  try {
    int result = 12 ~/ 0; // Integer division by zero
    print(result);
  } on IntegerDivisionByZeroException {
    print('Cannot divide by zero');
  } catch (e) {
    print('An error occurred: $e');
  } finally {
    print('This always executes');
  }
}
```

## 2. Throwing Exceptions

You can throw exceptions using the `throw` keyword.

### Example:

```dart
void validateAge(int age) {
  if (age < 0) {
    throw ArgumentError('Age cannot be negative');
  }
}

void main() {
  try {
    validateAge(-5);
  } catch (e) {
    print('Error: $e');
  }
}
```

## 3. Custom Exceptions

You can create custom exception classes by extending `Exception` or `Error`.

### Example:

```dart
class InsufficientFundsException implements Exception {
  final double balance;
  final double withdrawal;

  InsufficientFundsException(this.balance, this.withdrawal);

  @override
  String toString() => 'Insufficient funds: $balance available, $withdrawal requested';
}

void withdraw(double balance, double amount) {
  if (amount > balance) {
    throw InsufficientFundsException(balance, amount);
  }
  // Proceed with withdrawal
}

void main() {
  try {
    withdraw(100, 150);
  } on InsufficientFundsException catch (e) {
    print(e);
  }
}
```

## 4. Async Error Handling

When working with asynchronous code, you can use try-catch with async-await.

### Example:

```dart
Future<String> fetchData() async {
  // Simulating network request
  await Future.delayed(Duration(seconds: 2));
  throw Exception('Failed to fetch data');
}

void main() async {
  try {
    String data = await fetchData();
    print(data);
  } catch (e) {
    print('Error occurred: $e');
  }
}
```

## 5. Error Propagation

You can rethrow exceptions to propagate them up the call stack.

### Example:

```dart
void processData(String data) {
  try {
    // Process data
    throw FormatException('Invalid data format');
  } catch (e) {
    print('Error in processData: $e');
    rethrow; // Rethrow the caught exception
  }
}

void main() {
  try {
    processData('some data');
  } catch (e) {
    print('Error in main: $e');
  }
}
```

## 6. Using `assert`

The `assert` statement is used to check boolean conditions during development.

### Example:

```dart
void setVolume(int volume) {
  assert(volume >= 0 && volume <= 100, 'Volume must be between 0 and 100');
  // Set volume
}

void main() {
  setVolume(50); // OK
  setVolume(150); // AssertionError in debug mode
}
```

Note: Assertions are only enabled in debug mode and are ignored in production code.

## Conclusion

Effective error handling is essential for creating robust Dart applications. By using these techniques, you can gracefully handle exceptions, provide meaningful error messages, and improve the reliability of your code.