شرح كيفية استخدام الapi مع repostery في Flutter وتنظيم الكود
يعد استخدام الapi شيئ اساسي في عمل الشركات لانك تتعامل مع سيرفرات في نقل واستقبال البيانات ويكون هناك شخص Backend مختص بهذه العمليه وياتي دورنا كمطورين flutter لربط الapi مع الapplication ويوجد اكثر من طريقة للربط ولكن الاشهر هيا الdio والتي تم شرحها في دروس سابقة وفي هذا الدرس سوف نشرحها ولكن بشكل منظم اكثر وهيا باستخدام ملف الrepostery لتنظيم العملية .
يعد محرك Flutter ومكتبة Foundation وعناصر واجهة المستخدم جزءًا من إطار عمل Flutter ، والذي تم إنشاؤه بلغة برمجة Dart. يختلف نهج Flutter في التطوير عن الآخرين من حيث أنه يستخدم كتابة تعريفية لواجهة المستخدم. هناك حاجة للبدء من النهاية هنا ، مما يعني أنه قبل البدء في تصميم أي قطعة ، يجب أن يكون لدى المستخدم فكرة كاملة عن نوع واجهة المستخدم التي ستكون عليها. يعتبر العديد من المطورين أن كتابة واجهة المستخدم هذه أكثر وضوحًا ، ولكنها توفر بعض التحديات والمشاكل للمطورين في البداية.
تهيئة class لاستقبال العمليات من الapi
عليك ان تقوم اولا بتهيئة ملف الdio لكي يستقبل ويرسل معك البيانات يمكنك نسخ الكود التالي , وهنا قمنا بعمل get و post و delete وهذه اغلب انواع البيانات التي نتعامل معها خلال الapi يمكنك نسخ الكود التالي بدون تعديل واستخدامه .
dio.dart
import 'package:dio/dio.dart';
import 'dart:async';
abstract class DioHelper {
Future<dynamic> post({
required String url,
dynamic data,
String? token,
});
Future<dynamic> get({
required String url,
dynamic query,
String? token,
});
Future<dynamic> delete({
required String url,
dynamic data,
String? token,
});
}
class DioImpl extends DioHelper {
final Dio dio = Dio(
BaseOptions(
baseUrl: baseUrl,
receiveDataWhenStatusError: true,
),
);
@override
Future post({
required String url,
dynamic data,
String? token,
}) async {
dio.options.headers = {
'Content-Type': 'application/json',
if (token != null) 'Authorization': 'Bearer $token'
};
if (url.contains('??')) {
url = url.replaceAll('??', '?');
}
print('URL => ${dio.options.baseUrl + url}');
print('Header => ${dio.options.headers.toString()}');
print('Body => $data');
return await request(
() async => await dio.post(url, data: data),
);
}
@override
Future get({required String url, query, String? token}) async {
dio.options.headers = {
'Content-Type': 'application/json',
if (token != null) 'Authorization': 'Bearer $token'
};
if (url.contains('??')) {
url = url.replaceAll('??', '?');
}
print('URL => ${dio.options.baseUrl + url}');
print('Header => ${dio.options.headers.toString()}');
print('Body => $query');
return await request(
() async => await dio.get(url, queryParameters: query),
);
}
@override
Future delete({required String url, data, String? token}) async {
dio.options.headers = {
'Content-Type': 'application/json',
if (token != null) 'Authorization': 'Bearer $token'
};
if (url.contains('??')) {
url = url.replaceAll('??', '?');
}
return await request(
() async => await dio.delete(url, queryParameters: data),
);
}
}
extension on DioHelper {
Future request(Future<Response> Function() request) async {
try {
final r = await request.call();
print("Response => ${r.data}");
return r;
} on DioError catch (e) {
print("Error => ${e.response!.data['error']}");
throw ServerException(
error: e.response!.data['error'],
code: e.response!.statusCode!,
);
} catch (e) {
throw Exception();
}
}
كيفية تخطي الerror الذي يتم الحصول عليه من الapi في بعض الوقت
احيانا نحصل على مشاكل في الapi ولهذا نحتاج ان نقوم بتهيئة ملفات للتعامل مع فشل البيانات التي نحصل عليها من الapi وكما هو موضح في الصوره , يمكنك نسخ الاكواد التاليه وعمل ملفات بالاسماء الخاصه بهم كما هو موضح في الصوره والاكواد لا يوجد بها اي تغيير .
Exception.dart
class ServerException implements Exception {
final String error;
final int code;
ServerException({
required this.error,
required this.code,
});
}
class CacheException implements Exception {
final dynamic error;
CacheException(this.error);
}
Failure.dart
abstract class Failure extends Equatable {
@override
List<Object> get props => [];
}
// General failures
class ServerFailure extends Failure {
final String error;
final int code;
ServerFailure({
required this.error,
required this.code,
});
}
class CacheFailure extends Failure {}
String mapFailureToMessage(Failure failure) {
switch (failure.runtimeType) {
case ServerFailure:
return (failure as ServerFailure).error.toString();
case CacheFailure:
return cacheFailureMessage;
default:
return 'Unexpected error';
}
}
int mapFailureToCode(Failure failure) => (failure as ServerFailure).code;
إنشاء class للعنوان النهائي للapi
الان نقوم بعمل ملف يحتوي على كل الend point التي نحتاجها في الكود وايضا في الاعلى نضع الرابط الرئيسي او الbase url كما هو متعارف عليه .
endPoint.dart
const baseUrl = 'https://###/';
const categoryNameUrl = 'category';
const updateProfileUrl = 'update_profile';
const orderUrl = 'order';
const homeDatakUrl = 'home_data';
كتابة logic الapi بداخل الRepository
الان ناتي لاهم خطوة في المقالة وهيا تهيئة ملف الRepository وهنا نقوم بعمل class الRepository ونضع به ونضع به نوع البيانات التي نريدها والتي نريد ارسالها بالنسبة اذا كان هناك token فلا نحتاج الى ان نقوم بكتابته وبعدها نقوم بعمل class للRepoImplementation وهنا نقوم بكتابة الكود ونرسل له البيانات التي يحتاجها سواء token او data ولكن هنا قمنا بوضع الend url مختلف عن الصفحة السابقة وهذا الامر خاطئ ولكن يمكنك استخدام الendPoint الموجوده من الاعلى في مكان الurl . وهنا وضعنا لكم اكثر من مثال لاكثر من نوع بيانات لايضاح الفكرة .
Repository.class
abstract class Repository {
Future<Response> getHomeRepo();
Future<Response> getOrderDetailsRepo({
required int id,
});
Future<Response> categoryDetailsRepo({
required String categoryName,
});
Future<Response> updateAccountRepo({
required String name,
required String email,
required String phone,
File? image,
});
class RepoImplementation extends Repository {
final DioHelper dioHelper;
final CacheHelper cacheHelper;
RepoImplementation({
required this.dioHelper,
required this.cacheHelper,
});
@override
Future<Response> getHomeRepo() async {
return await dioHelper.get(
url: homeData,
);
}
@override
Future<Response> getOrderDetailsRepo({
required int id
}) async {
return await dioHelper.get(
url: '$order$id',
token: token,
);
}
@override
Future<Response> categoryDetailsRepo({
required String categoryName,
}) async {
return await dioHelper.get(
url: categoryUrl,
query: {
'category': categoryName,
},
);
}
@override
Future<Response> updateAccountRepo({
required String name,
required String email,
required String phone,
File? image,
}) async {
return await dioHelper.post(
url: updateProfile,
token: token,
data: FormData.fromMap(
{
'name': name,
'email': email,
'phone': phone,
if (image != null)
'image': await MultipartFile.fromFile(
image.path,
filename: Uri.file(image.path).pathSegments.last,
),
},
),
);
}
التعامل مع الapi بإستخدام الbloc
الان ناتي للكود الموجود داخل ملف الbloc ونقوم بكتابة اسم الmodel كما هو موضح وبعدها ناخذ منه object وبعدها نكتب الmethode المسؤوله عن عملية جلب البيانات كما هو موضح ونطبع العمليات للتاكد من عملها بشكل جيد وتشغيلها بداخل الmain وتحديدا داخل الblocProvider اذا كنت تريد اختبار model .
cubit.dart
// categoryDetails ------------------- start
TopBorrowModel? categoryDetailsModel;
void categoryDetails({
required String categoryName,
}) async {
categoryDetailsModel = null;
debugPrint('categoryDetails------------loading');
emit(CategoryLoading());
await _repository
.categoryDetailsRepo(categoryName: categoryName)
.then((value) {
// success
categoryDetailsModel = TopBorrowModel.fromJson(value.data);
debugPrint('categoryDetails------------success');
emit(CategorySuccess());
}).catchError((error) {
// error
debugPrint('categoryDetails------------error');
debugPrint(error.toString());
emit(Error(error.toString()));
});
}
// categoryDetails ------------------- end
إنشاء حالات داخل ملف state
الخطوة الاخيره وهيا من المفتروض عملها قبل الخطوة السابقة وهيا تهيئة ملف الstate عن طريق وضع علامة لتحميل البيانات ونجاحها وفشلها والفشل هنا يكون مره واحده فقط ولن نحتاج لاعادته في المرات المقبلة في العمليات ونكتفي بالنجاح وانتظار تحميل البيانات .
state.dart
@immutable
abstract class MainState {}
class CategoryLoading extends MainState {}
class CategorySuccess extends MainState {}
class Error extends MainState {
final String error;
Error(this.error);
}
}
لمزيد من مقالات فلاتر يمكنك متابعة احد المقالات التاليه :