feat:dio client done

This commit is contained in:
jahongireshonqulov
2025-10-28 17:03:13 +05:00
parent c528f7a07e
commit 4c652c2b47
18 changed files with 336 additions and 3 deletions

View File

@@ -0,0 +1,3 @@
abstract class ApiConst {
static const String baseUrl = "https://superapp.felixits.uz/api/v1";
}

View File

@@ -1,4 +1,5 @@
export 'app_locale_keys.dart';
export 'sizes_consts.dart';
export 'l10n.dart';
export 'time_delay_cons.dart';
export 'time_delay_cons.dart';
export 'api_const.dart';

View File

@@ -2,6 +2,6 @@ abstract class TimeDelayConst {
static const Duration durationMill150 = Duration(milliseconds: 150);
static const Duration durationMill300 = Duration(milliseconds: 300);
static const Duration durationMill800 = Duration(milliseconds: 800);
static const Duration durationMill3500 = Duration(milliseconds: 3500);
static const Duration duration3 = Duration(seconds: 3);
}

View File

@@ -11,4 +11,6 @@ export 'l10n/app_localizations_en.dart';
export 'l10n/app_localizations_ru.dart';
export 'l10n/app_localizations_uz.dart';
export 'services/services.dart';
export 'utils/app_utils.dart';
export 'utils/app_utils.dart';
export 'exceptions/exceptions.dart';
export 'exceptions/failure.dart';

View File

@@ -9,6 +9,7 @@
// coverage:ignore-file
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:dio/dio.dart' as _i361;
import 'package:get_it/get_it.dart' as _i174;
import 'package:injectable/injectable.dart' as _i526;
@@ -24,7 +25,9 @@ import '../../feature/main/presentation/blocs/main_bloc/main_bloc.dart'
import '../../feature/on_boarding/presentation/blocs/splash_bloc/splash_bloc.dart'
as _i311;
import '../../food_delivery_client.dart' as _i321;
import '../network/dio_client.dart' as _i667;
import '../router/app_routes.dart' as _i152;
import '../services/request_handler_service.dart' as _i354;
import '../services/storage_service.dart' as _i306;
extension GetItInjectableX on _i174.GetIt {
@@ -34,6 +37,7 @@ extension GetItInjectableX on _i174.GetIt {
_i526.EnvironmentFilter? environmentFilter,
}) {
final gh = _i526.GetItHelper(this, environment, environmentFilter);
final dioModule = _$DioModule();
gh.factory<_i1007.HomeBloc>(() => _i1007.HomeBloc());
gh.factory<_i728.BasketBloc>(() => _i728.BasketBloc());
gh.factory<_i991.BrowseBloc>(() => _i991.BrowseBloc());
@@ -41,9 +45,16 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i311.SplashBloc>(() => _i311.SplashBloc());
gh.singleton<_i306.StorageService>(() => _i306.StorageService());
gh.singleton<_i152.AppRoutes>(() => _i152.AppRoutes());
gh.lazySingleton<_i667.DioClient>(() => _i667.DioClient());
gh.factory<_i942.LanguageBloc>(
() => _i942.LanguageBloc(gh<_i321.StorageService>()),
);
gh.lazySingleton<_i361.Dio>(() => dioModule.dio(gh<_i667.DioClient>()));
gh.singleton<_i354.RequestHandlerService>(
() => _i354.RequestHandlerService(gh<_i361.Dio>()),
);
return this;
}
}
class _$DioModule extends _i667.DioModule {}

View File

@@ -0,0 +1,32 @@
import 'package:dio/dio.dart';
class ServerException implements Exception {
final String errorMessage;
final String errorKey;
final num statusCode;
const ServerException({
required this.statusCode,
required this.errorMessage,
required this.errorKey,
});
@override
String toString() {
return 'ServerException(statusCode: $statusCode, errorMessage: $errorMessage, errorKey: $errorKey)';
}
}
class CustomDioException implements Exception {
final String errorMessage;
final DioExceptionType type;
final int? statusCode;
CustomDioException({required this.errorMessage, required this.type, this.statusCode});
}
class ParsingException implements Exception {
final String errorMessage;
const ParsingException({required this.errorMessage});
}

View File

@@ -0,0 +1,44 @@
import 'package:equatable/equatable.dart';
import 'package:dio/dio.dart';
class Failure extends Equatable {
final String? errorMessage;
final String? errorKey;
const Failure({
this.errorMessage,
this.errorKey,
}); //error key kere bomasa required qilish shartamas
@override
List<Object?> get props => [
errorMessage,
errorKey,
];
}
class ServerFailure extends Failure {
final num statusCode;
const ServerFailure({
required super.errorMessage,
required this.statusCode,
required super.errorKey,
});
}
class DioFailure extends Failure {
final DioExceptionType type;
final int? statusCode;
const DioFailure({
required super.errorMessage,
this.type = DioExceptionType.badResponse,
this.statusCode,
});
}
class ParsingFailure extends Failure {
const ParsingFailure({required super.errorMessage});
}
class CacheFailure extends Failure {}

View File

@@ -0,0 +1,44 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:food_delivery_client/core/network/header_interceptors.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
import 'error_handler_interceptor.dart';
@module
abstract class DioModule {
@lazySingleton
Dio dio(DioClient dioClient) => dioClient.dio;
}
@lazySingleton
class DioClient {
final BaseOptions _dioBaseOptions = BaseOptions(
baseUrl: ApiConst.baseUrl,
connectTimeout: TimeDelayConst.durationMill3500,
receiveTimeout: TimeDelayConst.durationMill3500,
followRedirects: false,
validateStatus: (status) => status != null && status >= 200 && status < 300,
);
Dio get dio {
final Dio dio = Dio(_dioBaseOptions);
dio.interceptors
..add(HeaderInterceptors())
..add(ErrorHandlerInterceptors())
..add(
LogInterceptor(
requestHeader: true,
requestBody: true,
responseBody: true,
error: true,
request: true,
logPrint: (object) {
if (kDebugMode) {
log('Error ${object.toString()}');
}
},
),
);
return dio;
}
}

View File

@@ -0,0 +1,41 @@
import 'package:dio/dio.dart';
import '../../feature/common/data/models/error_model.dart';
class ErrorHandlerInterceptors extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (err.response != null && err.response!.statusCode != null) {
if (err.response?.data is String) {
final errorMessage = err.response?.data as String?;
final error = ErrorModel(
detail: errorMessage?.replaceAll(RegExp(r'<[^>]+>'), '') ?? '',
);
handler.next(
DioException(
requestOptions: err.requestOptions,
response: Response(
requestOptions: err.requestOptions,
data: error.toJson(),
),
),
);
return;
} else {
handler.next(err);
return;
}
}
const error = ErrorModel(detail: "");
handler.next(
DioException(
requestOptions: err.requestOptions,
response: Response(
requestOptions: err.requestOptions,
data: error.toJson(),
),
),
);
return;
}
}

View File

@@ -0,0 +1,17 @@
import 'package:dio/dio.dart';
class HeaderInterceptors extends Interceptor {
HeaderInterceptors._internal();
static final HeaderInterceptors _interceptors =
HeaderInterceptors._internal();
factory HeaderInterceptors() {
return _interceptors;
}
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
handler.next(options);
}
}

View File

@@ -0,0 +1,77 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
import '../../feature/common/data/models/error_model.dart';
enum RequestMethodEnum { get, post, put, delete, patch }
@singleton
class RequestHandlerService {
final Dio dio;
const RequestHandlerService(this.dio);
Future<T> handleRequest<T>({
required Future<T> Function(Response) fromJson,
required String path,
RequestMethodEnum? method,
Options? options,
Map<String, dynamic>? queryParameters,
Object? data,
Dio? newDio,
}) async {
try {
final response = await (newDio ?? dio).request(
path,
options:
options ??
Options(method: method?.name ?? RequestMethodEnum.get.name),
queryParameters: queryParameters,
data: data,
);
final result = fromJson.call(response);
return result;
} on DioException catch (e) {
final errorResponse = ErrorModel.fromJson(e.response?.data);
throw CustomDioException(
errorMessage: errorResponse.detail,
type: e.type,
statusCode: e.response?.statusCode,
);
} on ParsingException {
rethrow;
} on Exception catch (e) {
throw ParsingException(errorMessage: e.toString());
} catch (e) {
throw ParsingException(errorMessage: e.toString());
}
}
Future<Either<Failure, T>> handleRequestInRepository<T>({
required Future<T> Function() onRequest,
String debugLabel = '',
}) async {
try {
final result = await onRequest.call();
return Right(result);
} on ParsingException catch (e) {
log("ParsingException in $debugLabel: ${e.errorMessage}");
return Left(ParsingFailure(errorMessage: e.errorMessage));
} on CustomDioException catch (e) {
log("CustomDioException in $debugLabel: ${e.errorMessage}");
return Left(
DioFailure(
errorMessage: e.errorMessage,
type: e.type,
statusCode: e.statusCode,
),
);
} on Exception catch (e) {
log("Exception in $debugLabel: ${e.toString()}");
return Left(ParsingFailure(errorMessage: e.toString()));
}
}
}

View File

@@ -0,0 +1,15 @@
import 'package:dartz/dartz.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
mixin UseCase<Type, Params> {
Future<Either<Failure, Type>> call(Params params);
}
mixin StreamUseCase<Type, Params> {
Stream<Either<Failure, Type>> call(Params params);
}
class NoParams extends Equatable {
@override
List<Object?> get props => [];
}

View File

@@ -0,0 +1,20 @@
import '../../domain/entities/error_entity.dart';
class ErrorModel extends ErrorEntity {
const ErrorModel({super.detail});
Map<String, dynamic> toJson() {
return {"detail": detail};
}
factory ErrorModel.fromJson(Map<String, dynamic> data) {
if (data.containsKey("detail")) {
if (data['detail'] is List) {
return ErrorModel(detail: data['detail'][0]["msg"]);
} else {
return ErrorModel(detail: data['detail']);
}
}
return ErrorModel(detail: data['message']);
}
}

View File

@@ -0,0 +1,5 @@
class ErrorEntity {
final String detail;
const ErrorEntity({this.detail = ''});
}

View File

@@ -22,3 +22,4 @@ export 'package:carousel_slider/carousel_slider.dart';
export 'package:flutter_bounceable/flutter_bounceable.dart';
export 'package:food_delivery_client/feature/on_boarding/presentation/pages/splash_page/splash_page.dart';
export 'package:skeletonizer/skeletonizer.dart';
export 'package:equatable/equatable.dart';

View File

@@ -201,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
dartz:
dependency: "direct main"
description:
name: dartz
sha256: e6acf34ad2e31b1eb00948692468c30ab48ac8250e0f0df661e29f12dd252168
url: "https://pub.dev"
source: hosted
version: "0.10.1"
dio:
dependency: "direct main"
description:
@@ -217,6 +225,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
equatable:
dependency: "direct main"
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:

View File

@@ -20,6 +20,8 @@ dependencies:
flutter_bounceable: ^1.2.0
cached_network_image: ^3.4.1
carousel_slider: ^5.1.1
equatable: ^2.0.7
dartz: ^0.10.1
#DI
get_it: ^8.2.0
@@ -29,6 +31,8 @@ dependencies:
#network
dio: ^5.9.0
#to use svgs
flutter_svg: ^2.2.1