Created
July 30, 2023 12:54
-
-
Save OmarYehiaDev/eaeb2c051d07ff23a5e304b3b46b982d to your computer and use it in GitHub Desktop.
Error handling in Flutter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:io'; | |
import 'package:dio/dio.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:laravel_exception/laravel_exception.dart'; | |
import 'error_response.dart'; | |
import 'constants.dart'; | |
import 'enums.dart'; | |
class ApiErrorParser { | |
final Exception exception; | |
final List<int>? skipCodes; | |
ApiErrorParser({ | |
required this.exception, | |
this.skipCodes, | |
}) { | |
assert( | |
skipCodes == null || (skipCodes != null && skipCodes!.isNotEmpty), | |
"You must provide a skip code or make it null!", | |
); | |
_parseError(); | |
} | |
ErrorResponse? _error; | |
ErrorResponse? get error => _error; | |
ErrorResponse? _parseError() { | |
if ((exception is PlatformException)) return _parsePlatformException(); | |
if ((exception is! DioError)) return _parseGeneralError(); | |
switch ((exception as DioError).type) { | |
case DioErrorType.response: | |
return _parseApiResponseError(); | |
case DioErrorType.connectTimeout: | |
return _parseInternetConnectionError(); | |
case DioErrorType.sendTimeout: | |
return _parseServerConnectionError(); | |
case DioErrorType.receiveTimeout: | |
return _parseServerConnectionError(); | |
case DioErrorType.other: | |
return _parseOtherDioError(); | |
default: | |
return _parseGeneralError(); | |
} | |
} | |
bool checkSkipCodes(int code) { | |
return skipCodes != null && skipCodes!.isNotEmpty && skipCodes!.contains(code); | |
} | |
ErrorResponse? _parseApiResponseError() { | |
switch ((exception as DioError).response!.statusCode) { | |
case HttpStatus.notFound: | |
if (checkSkipCodes(HttpStatus.notFound)) return null; | |
return _parseNotFoundError(); | |
case HttpStatus.unprocessableEntity: | |
if (checkSkipCodes(HttpStatus.unprocessableEntity)) return null; | |
return _parseValidationError(); | |
case HttpStatus.internalServerError: | |
if (checkSkipCodes(HttpStatus.internalServerError)) return null; | |
return _parseInternalServerError(); | |
case HttpStatus.badRequest: | |
if (checkSkipCodes(HttpStatus.badRequest)) return null; | |
return _parseValidationError(); | |
case HttpStatus.unauthorized: | |
if (checkSkipCodes(HttpStatus.unauthorized)) return null; | |
return _parseValidationError(); | |
default: | |
return _parseGeneralError(); | |
} | |
} | |
ErrorResponse _parseNotFoundError() { | |
LNotFoundException e = LNotFoundException.parse( | |
(exception as DioError).response!.data, | |
); | |
final err = ErrorResponse(message: e.message, type: ErrorType.notFoundError); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parsePlatformException() { | |
final err = ErrorResponse( | |
message: (exception as PlatformException).details?["response_message"], | |
type: ErrorType.validationError, | |
); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parseInternalServerError() { | |
LServerException e = LServerException( | |
response: (exception as DioError).response!.data, | |
); | |
final err = ErrorResponse(message: e.message, type: ErrorType.internalServerError); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parseValidationError() { | |
final e = LValidationException( | |
(exception as DioError).response!.data, | |
); | |
List<String> errors = Constants.getLaravelMultipleValidationErrors(e); | |
final err = ErrorResponse( | |
message: errors.isEmpty ? e.message : errors.join("\n"), | |
type: ErrorType.validationError, | |
); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parseInternetConnectionError() { | |
final err = ErrorResponse(message: "", type: ErrorType.internetConnection); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parseServerConnectionError() { | |
final err = ErrorResponse(message: "", type: ErrorType.serverConnection); | |
_error = err; | |
return err; | |
} | |
ErrorResponse _parseOtherDioError() { | |
if ((exception as DioError).error is SocketException) { | |
if (((exception as DioError).error as SocketException).osError!.errorCode == 104) { | |
return _parseServerConnectionError(); | |
} | |
return _parseInternetConnectionError(); | |
} | |
return _parseGeneralError(); | |
} | |
ErrorResponse _parseGeneralError() { | |
final err = ErrorResponse( | |
message: exception.toString(), | |
type: ErrorType.generalError, | |
); | |
_error = err; | |
return err; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:laravel_exception/laravel_exception.dart'; | |
import 'services/locale/app_localizations.dart'; | |
import 'generated/locale_keys.g.dart'; | |
import 'api_error_parser.dart'; | |
import 'error_response.dart'; | |
import 'enums.dart'; | |
class Constants { | |
Constants._(); | |
static void showErrorSnackBar(ScaffoldMessengerState context, String msg) { | |
context.showSnackBar( | |
SnackBar( | |
backgroundColor: Colors.red, | |
content: Text( | |
msg, | |
), | |
), | |
); | |
} | |
static void showSucessSnackBar(ScaffoldMessengerState context, String msg) { | |
context.showSnackBar( | |
SnackBar( | |
backgroundColor: Colors.green, | |
content: Text( | |
msg, | |
), | |
), | |
); | |
} | |
static List<String> getLaravelMultipleValidationErrors(LValidationException exception) { | |
List<List<String>> errorsList = List.generate( | |
exception.keys.length, | |
(index) => exception.errorsByKey(exception.keys[index]) ?? [], | |
); | |
errorsList.removeWhere((element) => element.isEmpty); | |
List<String> errors = []; | |
for (List<String> element in errorsList) { | |
String errorStr = element.length == 1 ? element.first : element.join("\n"); | |
errors.add(errorStr); | |
} | |
return errors; | |
} | |
static void handleErrorSnackBar( | |
Exception e, | |
ScaffoldMessengerState messengerState, | |
AppLocalizations translator, { | |
List<int>? skipCodes, | |
}) { | |
final parser = ApiErrorParser(exception: e, skipCodes: skipCodes); | |
final ErrorResponse? error = parser.error; | |
if (error == null) return; | |
if (error.type == ErrorType.internetConnection) { | |
Constants.showErrorSnackBar( | |
messengerState, | |
translator.translate(LocaleKeys.connectionError), | |
); | |
return; | |
} | |
if (error.type == ErrorType.serverConnection) { | |
Constants.showErrorSnackBar( | |
messengerState, | |
translator.translate(LocaleKeys.serverConnectionError), | |
); | |
return; | |
} | |
Constants.showErrorSnackBar(messengerState, error.message); | |
return; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum ErrorType { | |
internetConnection, | |
serverConnection, | |
internalServerError, | |
notFoundError, | |
validationError, | |
generalError, | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ignore_for_file: invalid_annotation_target | |
import 'package:freezed_annotation/freezed_annotation.dart'; | |
import 'enums.dart'; | |
part 'error_response.freezed.dart'; | |
part 'error_response.g.dart'; | |
@freezed | |
class ErrorResponse with _$ErrorResponse { | |
factory ErrorResponse({ | |
required final String message, | |
required final ErrorType type, | |
}) = _ErrorResponse; | |
factory ErrorResponse.fromJson(Map<String, dynamic> json) => _$ErrorResponseFromJson(json); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment