Errors & Debugging in Dart
In this lesson, you will learn how to handle errors and perform debugging in Dart applications. Error handling is critical for building stable, reliable, and production-ready software.
A well-written Dart program should never crash unexpectedly. Instead, it should handle errors gracefully and provide useful feedback.
What Are Errors?
Errors are problems that occur during program execution. They can happen due to invalid input, logic mistakes, missing files, network failures, or unexpected system behavior.
Dart errors are mainly divided into:
- Compile-time errors
- Runtime errors (Exceptions)
Compile-Time Errors
Compile-time errors occur when the code violates Dart syntax or type rules. These errors are detected before the program runs.
void main() {
int x = "10"; // Error: String cannot be assigned to int
}
These errors must be fixed before execution.
Runtime Errors (Exceptions)
Runtime errors occur while the program is running. Dart represents runtime errors using exceptions.
Example: dividing a number by zero.
void main() {
int result = 10 ~/ 0;
print(result);
}
Handling Exceptions Using try-catch
Use try and catch blocks to handle runtime exceptions safely.
void main() {
try {
int result = 10 ~/ 0;
print(result);
} catch (e) {
print("Error occurred: $e");
}
}
The program continues running instead of crashing.
Handling Specific Exceptions
You can catch specific exception types for better control.
void main() {
try {
int value = int.parse("abc");
print(value);
} on FormatException {
print("Invalid number format");
}
}
Using finally Block
The finally block always executes, whether an error occurs or not.
This is useful for cleanup tasks like closing files or connections.
void main() {
try {
print("Processing...");
throw Exception("Something went wrong");
} catch (e) {
print("Caught error");
} finally {
print("Cleanup completed");
}
}
Throwing Custom Exceptions
You can manually throw exceptions using the throw keyword.
void checkAge(int age) {
if (age < 18) {
throw Exception("Age must be 18 or above");
}
}
void main() {
try {
checkAge(16);
} catch (e) {
print(e);
}
}
Creating Custom Exception Classes
For large applications, custom exception classes improve clarity.
class InvalidBalanceException implements Exception {
String message;
InvalidBalanceException(this.message);
}
void withdraw(int balance) {
if (balance < 100) {
throw InvalidBalanceException("Insufficient balance");
}
}
void main() {
try {
withdraw(50);
} catch (e) {
print(e);
}
}
Debugging Using print()
The simplest debugging technique is printing values.
void main() {
int a = 5;
int b = 10;
print("a = $a, b = $b");
}
Using Assertions for Debugging
Assertions help detect invalid states during development.
void main() {
int age = 15;
assert(age >= 18, "Age must be at least 18");
}
Assertions run only in debug mode.
Common Debugging Strategies
- Print intermediate values
- Check null safety warnings
- Use meaningful error messages
- Handle exceptions properly
Real-World Example: User Login Validation
void login(String username, String password) {
if (username.isEmpty || password.isEmpty) {
throw Exception("Username or password cannot be empty");
}
print("Login successful");
}
void main() {
try {
login("", "1234");
} catch (e) {
print("Login failed: $e");
}
}
📝 Practice Exercises
Exercise 1
Write a program that catches division by zero.
Exercise 2
Create a custom exception for invalid marks.
Exercise 3
Use finally to display a completion message.
✅ Practice Answers
class InvalidMarksException implements Exception {
String message;
InvalidMarksException(this.message);
}
void checkMarks(int marks) {
if (marks < 0 || marks > 100) {
throw InvalidMarksException("Marks must be between 0 and 100");
}
}
void main() {
try {
checkMarks(120);
} catch (e) {
print(e);
} finally {
print("Validation completed");
}
}
What’s Next?
In the next lesson, you will learn about Dart Project Structure and how professional Dart applications are organized.