Skip to content

Instantly share code, notes, and snippets.

@OmarYehiaDev
Created July 30, 2023 12:54
Show Gist options
  • Save OmarYehiaDev/eaeb2c051d07ff23a5e304b3b46b982d to your computer and use it in GitHub Desktop.
Save OmarYehiaDev/eaeb2c051d07ff23a5e304b3b46b982d to your computer and use it in GitHub Desktop.
Error handling in Flutter
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;
}
}
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;
}
}
enum ErrorType {
internetConnection,
serverConnection,
internalServerError,
notFoundError,
validationError,
generalError,
}
// 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