Error Handling
The mobile client employs a standardized and robust error handling strategy to ensure that issues are managed gracefully and that users are presented with clear, helpful feedback. The strategy is built upon a set of custom exception classes defined in the shared core
package.
The Standardized Exceptions
Section titled “The Standardized Exceptions”All exceptions that represent business logic or data fetching errors are subtypes of HttpException
. This provides a consistent set of errors that can be handled across the entire application. Examples include:
NotFoundException
: Thrown when a requested resource does not exist.NetworkException
: Thrown for connectivity issues like timeouts or DNS failures.UnauthorizedException
: Thrown for authentication or authorization failures.OperationFailedException
: A general-purpose exception for other server-side or unexpected failures.
The Flow of an Exception
Section titled “The Flow of an Exception”The error handling strategy follows the application’s layered architecture, ensuring that exceptions are caught and handled at the appropriate level.
-
Data Layer: The
DataApi
client is responsible for making HTTP requests. It wraps alltry/catch
blocks around these requests. If aDioException
(from thehttp_client
package) occurs, anErrorInterceptor
maps it to one of the standardizedHttpException
subtypes. This is the only layer where raw network exceptions are handled. -
Repository Layer: The
DataRepository
calls the data clients. It does not typically handle exceptions itself but allows them to propagate upwards. This keeps the repository focused on its role as a data aggregator. -
Business Logic Layer (BLoC): The BLoC is where exceptions are ultimately handled. A BLoC will wrap its calls to the repository in a
try/catch
block.- If an operation is successful, it emits a
Success
state with the fetched data. - If an
HttpException
is caught, it emits aFailure
state, capturing the exception.
lib/headlines-feed/bloc/headlines_feed_bloc.dart // ...try {final headlineResponse = await _headlinesRepository.readAll(/* ... */);// ...emit(state.copyWith(status: HeadlinesFeedStatus.success, /* ... */));} on HttpException catch (e) {emit(state.copyWith(status: HeadlinesFeedStatus.failure, error: e));}// ... - If an operation is successful, it emits a
-
Presentation Layer (UI): The UI uses a
BlocBuilder
to listen for state changes from the BLoC.- When it receives a
Success
state, it renders the data. - When it receives a
Failure
state, it displays a user-friendly error message using the sharedFailureStateWidget
. This widget takes the exception from the state and uses a helper method (toFriendlyMessage
) to convert it into a localized, human-readable string.
- When it receives a
This structured approach ensures that UI components are never directly responsible for error handling logic, leading to a cleaner and more maintainable codebase.