๐Ÿ›ก๏ธ Lesson 6: Error Handling

โฑ 30-40 min ๐Ÿ“Š Intermediate ๐Ÿ“– In-depth tutorial ๐Ÿ”— Official Source
1

Why Error Handling Matters

๐Ÿง  Without error handling, any failure crashes your entire application

Users lose data, trust evaporates, and debugging becomes a nightmare. Proper error handling keeps your app running even when things go wrong, and provides clear feedback about what happened.

โŒ Without Handling

// User types invalid number
int age = int.parse('twenty');
// ๐Ÿ’ฅ APP CRASHES โ€” FormatException
// User sees nothing but a crash

๐Ÿ˜ฐ Data lost, user frustrated, app appears broken

โœ… With Handling

try {
  int age = int.parse(input);
} catch (e) {
  print('Please enter a valid number');
}
// โœ… App stays alive, user sees helpful message

๐Ÿ˜ App survives, user gets helpful guidance, trust maintained

2

Errors vs Exceptions โ€” The Critical Distinction

๐Ÿง  Dart distinguishes between two types of problems:

Understanding which is which determines whether you should fix your code or handle the failure gracefully.

๐Ÿ”ด Errors (Programmer Mistakes)

These indicate bugs that should not be caught. They mean your code is wrong and needs to be fixed.

  • TypeError โ€” calling a method on null
  • AssertionError โ€” assert condition fails
  • RangeError โ€” index out of bounds
  • NoSuchMethodError โ€” calling undefined method

Rule: DON'T catch errors. Fix the bug instead.

๐ŸŸก Exceptions (Expected Failures)

These are recoverable situations that should be caught and handled. They're not bugsโ€”they're expected possibilities.

  • FormatException โ€” invalid data format
  • IOException โ€” file or network failure
  • TimeoutException โ€” operation timed out
  • HttpException โ€” bad HTTP response

Rule: ALWAYS catch and handle exceptions.

๐ŸŽฎ Classify These Scenarios

๐Ÿ”ข Calling .length on null

var x; print(x.length);

๐Ÿ“ Parsing invalid JSON

json.decode('not json');

๐ŸŒ Network timeout

http.get() after 30 seconds

๐Ÿ“‹ Accessing list index -1

list[-1];

3

Try/Catch/Finally โ€” The Safety Net

๐Ÿง  The try/catch/finally structure catches exceptions and keeps your app alive

Think of it as a safety net under a trapeze artist. The try block is the performance, catch catches the fall, and finally cleans up regardless of what happened.

Complete Pattern with Line-by-Line Explanation

try {
  // Code that might throw an exception
  final file = File('data.txt');
  final contents = await file.readAsString();
  print('File loaded: \$contents');
  
} on FileSystemException catch (e) {
  // Catches specific file-related exceptions
  print('Could not read file: \${e.message}');
  
} on FormatException catch (e) {
  // Catches data format problems
  print('File contained invalid data: \$e');
  
} catch (e, stackTrace) {
  // Catches ALL other exceptions
  print('Unexpected error: \$e');
  print('Stack trace: \$stackTrace');
  
} finally {
  // ALWAYS runs โ€” perfect for cleanup
  print('Cleanup: closing resources');
}

๐Ÿ“ Structure Breakdown

1 try wraps risky code
2-4 Normal execution โ€” runs if no errors
6-8 on FileSystemException catches specific type
10-12 on FormatException catches another type
14-17 catch (e, stackTrace) catches everything else
16 stackTrace shows where error occurred
19-21 finally always runs, even after return

๐ŸŽฏ Click Each Block to Learn Its Purpose:

try { โ€” Code that might fail. Dart attempts to execute everything inside.
} on SpecificException catch (e) { โ€” Catch a specific exception type. Use when you know what might go wrong.
} catch (e, stackTrace) { โ€” Catch everything else. Always include as a safety net. stackTrace helps debugging.
} finally { โ€” Cleanup code. Runs whether there was an error or not. Close files, connections, etc.

โœ๏ธ Try It: Handle Different Error Types

Output will appear here...
4

Creating Custom Exceptions

๐Ÿง  When built-in exceptions aren't descriptive enough, create your own

Custom exceptions make your code more readable and help consumers understand exactly what went wrong. Implement Exception and override toString() for clear error messages.

๐Ÿ—๏ธ Build Your Exception

๐Ÿ“„ Generated Code

class ValidationException implements Exception {
  final String message;
  final String field;
  final dynamic value;
  
  ValidationException({
    required this.message,
    required this.field,
    this.value,
  });
  
  @override
  String toString() {
    return 'ValidationException: \$message (field: \$field)';
  }
}

Using Your Custom Exception

// Throwing your custom exception
void validateEmail(String email) {
  if (!email.contains('@')) {
    throw ValidationException(
      message: 'Invalid email format',
      field: 'email',
      value: email,
    );
  }
}

// Catching it specifically
try {
  validateEmail('not-an-email');
} on ValidationException catch (e) {
  print('Validation failed: \$e');
  // Validation failed: ValidationException: Invalid email format (field: email)
}
5

Throw vs Rethrow โ€” Propagating Errors

๐Ÿง  Throw creates a new exception. Rethrow preserves the original.

Use throw to raise a new exception with a fresh stack trace. Use rethrow (without arguments) inside a catch block to preserve the original stack traceโ€”crucial for debugging.

๐Ÿ“š Call Stack

main() โ€” starts the app
โ†’ processOrder()
โ†’ validatePayment()
โ†’ ๐Ÿ’ฅ EXCEPTION HERE

๐Ÿ“– Explanation

Click a button to see the difference...

๐Ÿ”ผ throw (New Exception)

try {
  riskyOperation();
} catch (e) {
  // Creates NEW exception โ€” loses original trace!
  throw Exception('Something went wrong');
}

โš ๏ธ Stack trace starts from here, original location lost!

๐Ÿ”„ rethrow (Preserve Original)

try {
  riskyOperation();
} catch (e) {
  print('Logging error before rethrowing');
  rethrow; // Preserves original stack trace!
}

โœ… Stack trace preserved โ€” shows exactly where error originated!

6

Real-World Error Handling Patterns

๐Ÿง  These are the patterns you'll use in production code

๐Ÿ“ Safe File Reading

Handle missing files gracefully

Click to test...

๐ŸŒ Network with Retry

Retry failed requests with backoff

Click to test...

๐Ÿ“Š Safe JSON Parsing

Handle malformed data

Click to test...

๐Ÿ“ User Input Validation

Validate and provide feedback

Click to test...
๐Ÿ“Œ Best Practices Summary:
โ€ข Catch specific exceptions first (on FormatException), then general (catch)
โ€ข Always include finally for resource cleanup (files, network connections)
โ€ข Never swallow exceptions silently โ€” at minimum, log them
โ€ข Use rethrow when you need to log but can't fully handle the error
โ€ข Create custom exceptions for domain-specific errors

๐ŸŽฏ Error Handling Quiz

Score: 0/5 | Hints: 3

Loading question...

๐Ÿš€

Practice Projects

Apply what you've learned by building these hands-on projects.

๐Ÿ“ฆ Project 1 ๐Ÿ“ฆ Project 2