SocketException or FormatException
There are many types of exceptions…
SocketException, FormatException or even IOException.
They all have clear purposes, telling you exactly what went wrong.
So how do we avoid exceptions that don’t do this?
Let’s dive into why meaningful exceptions matter and how they can save you hours of debugging frustration.
The dreaded “catch-all” approach.
throw HttpException('unauthorized')
// What should we catch?
For example, throwing a HttpException
with the message “unauthorized” during a sign-in process can lead to you catching all exceptions. This makes it hard for the calling function to handle specific exceptions.
This often results in a convoluted, harder-to-manage codebase.
Let’s dive into the approaches you should take to create your own exceptions.
The three types
There are three main topics we need to make sure we handle.
Clear naming, clear documentation and creating exceptions.
The name of the exception should be clear. If you are handling an “unauthenticated” scenario the exception could be named UnauthenticatedException
.
Naming is an important part of programming, but this holds especially true when it comes to naming exceptions. Clear names help identify the cause when thrown unexpectedly, making debugging and maintenance easier.
/// Divides two numbers.////// Throws:////// * [DivideByZeroException] if the [denominator] is zero.double divide(double numerator, double denominator) { if (denominator == 0) { throw DivideByZeroException('Denominator cannot be zero.'); } return numerator / denominator;}
Clear documentation is also critical.
When defining exceptions, make sure you are thinking about documenting the exceptions that a method or function can throw.
When you are later calling this method, you should be able to quickly ensure you handle all the possible exceptions.
class DivideByZeroException implements Exception { DivideByZeroException(this.message);
final String message;
@override String toString() => 'DivideByZeroException: $message';}
Now, how do we create custom exceptions?
Any exception you define, you must use the implements keyword. This forces you to implement required methods and properties the interface requires.
Creating custom exceptions
What about extending an exception?
That is valid too.
sealed class AuthException implements Exception { final String message;
AuthException(this.message);
@override String toString() => '${runtimeType.toString()}: $message';}
class InvalidEmailException extends AuthException { InvalidEmailException() : super('Invalid email address.');}
class InvalidPasswordException extends AuthException { InvalidPasswordException() : super('Incorrect password.');}
class UserNotFoundException extends AuthException { UserNotFoundException() : super('User not found.');}
There can be cases where you have an exception you want to create, but also want fine-control exceptions derived from that base exception.
Exceptions help delivering clear intent and documentation for your code.

Want to learn Flutter?
We teach you all you need to confidently build apps at hungrimind.com/learn/flutter
Guidelines
Now that was quite a lot of information, so let us simplify this.
- You want clear documentation of what exceptions a function can throw.
- You should define custom exceptions with clear, easy to understand names.
We created this small snippet library of valuable snippets you can use in your projects to comment your functions properly, and define your exceptions.
YouTube Video
Get articles right in your inbox
No spam, unsubscribe anytime. We treat your inbox with respect.