مقدمة حول التعامل مع REST APIs في Flutter
إذا كنت تبني تطبيق Flutter يتواصل بكثافة مع REST APIs، فأنت غالبًا تكتب الكثير من الأكواد المتكررة: طلبات GET و POST، إدارة الرؤوس headers، إرسال التوكنات، التعامل مع أخطاء الاتصال، معالجة الاستجابات، وإظهار رسائل مناسبة للمستخدم.
ومع توسّع التطبيق، يصبح وجود طبقة موحّدة للتعامل مع الشبكة أمرًا مهمًا جدًا. هنا تظهر فائدة مكتبة flutter_api_helper، فهي تساعدك على تنظيم طلبات HTTP وتقليل التكرار، مع توفير ميزات عملية مثل التخزين المؤقت، إعادة المحاولة، تسجيل الطلبات، وإدارة التوكنات.
بحسب صفحة الحزمة على pub.dev، فإن flutter_api_helper هي مكتبة Flutter سهلة الاستخدام للتعامل مع طلبات API، من الطلبات البسيطة إلى إدارة الأخطاء، التخزين المؤقت، وإدارة التوكنات.
ما هي مكتبة flutter_api_helper؟
flutter_api_helper هي حزمة Flutter/Dart تهدف إلى تبسيط التعامل مع REST APIs داخل تطبيقات Flutter. بدلًا من كتابة كود HTTP متكرر في كل شاشة أو خدمة، يمكنك الاعتماد على واجهة موحّدة لإرسال الطلبات وإدارة إعدادات الشبكة من مكان واحد.
توفر المكتبة عادةً مجموعة من الخصائص المهمة مثل:
- تنفيذ طلبات
GETوPOSTوPUTوDELETE. - إدارة عنوان API الأساسي
baseUrl. - إضافة الرؤوس الافتراضية
headers. - إدارة التوكنات وطلبات
Authorization. - تسجيل الطلبات والاستجابات أثناء التطوير.
- التعامل مع الأخطاء والاستثناءات بشكل موحّد.
- دعم التخزين المؤقت
caching. - دعم إعادة المحاولة
retryعند فشل الطلب.
تنويه مهم: قد تختلف أسماء الدوال أو بعض التواقيع البرمجية من إصدار لآخر، لذلك يُفضّل دائمًا الرجوع إلى صفحة الحزمة الرسمية على pub.dev قبل الاعتماد النهائي على أي مثال.
لماذا قد تختار flutter_api_helper؟
1. تقليل تكرار كود HTTP
بدلًا من كتابة نفس منطق الاتصال في كل مرة، مثل إعداد الرابط، الرؤوس، التوكن، وتحويل الاستجابة، يمكنك إنشاء إعداد مركزي ثم استخدام دوال مختصرة لتنفيذ الطلبات.
2. تنظيم طبقة الشبكة داخل التطبيق
وجود مكتبة أو Helper مخصص للتعامل مع APIs يجعل المشروع أسهل في الصيانة. فإذا أردت تعديل baseUrl أو مدة المهلة timeout أو طريقة التعامل مع الأخطاء، يمكنك فعل ذلك من مكان واحد بدلًا من تعديل عشرات الملفات.
3. تحسين التعامل مع الأخطاء
أحد أكبر تحديات تطبيقات Flutter التي تعتمد على REST APIs هو التعامل مع الأخطاء المختلفة، مثل انقطاع الإنترنت، انتهاء صلاحية التوكن، أخطاء الخادم، أو الاستجابات غير المتوقعة. مكتبة مثل flutter_api_helper تساعد على توحيد هذا السلوك لتقديم رسائل أوضح للمستخدم.
4. دعم التخزين المؤقت وإعادة المحاولة
في بعض الحالات لا تحتاج إلى جلب البيانات من الخادم في كل مرة، خاصة في صفحات مثل المنتجات، التصنيفات، الإعدادات، أو البيانات التي لا تتغير بسرعة. لذلك يكون التخزين المؤقت مفيدًا لتحسين الأداء وتقليل استهلاك الشبكة.
كذلك تساعد ميزة إعادة المحاولة في التعامل مع فشل الطلبات المؤقت، مثل ضعف الاتصال أو تأخر الخادم.
تثبيت مكتبة flutter_api_helper
لإضافة المكتبة إلى مشروع Flutter، افتح ملف pubspec.yaml وأضف الحزمة داخل قسم dependencies:
dependencies:
flutter_api_helper: ^1.0.0
بعد ذلك شغّل الأمر التالي:
flutter pub get
أو يمكنك إضافتها مباشرة من خلال الأمر:
flutter pub add flutter_api_helper
استيراد المكتبة داخل المشروع
بعد تثبيت الحزمة، يمكنك استيرادها في ملفات Dart التي تحتاج فيها إلى التعامل مع API:
import 'package:flutter_api_helper/flutter_api_helper.dart';
تهيئة flutter_api_helper في بداية التطبيق
من الأفضل تهيئة إعدادات API في بداية التطبيق، غالبًا داخل دالة main، حتى تكون الإعدادات متاحة في جميع أجزاء المشروع.
void main() {
ApiHelper.configure(
ApiConfig(
baseUrl: 'https://api.yourapp.com',
enableLogging: true,
timeout: const Duration(seconds: 30),
cacheConfig: CacheConfig(
duration: const Duration(minutes: 5),
),
retryConfig: RetryConfig(
maxRetries: 3,
),
),
);
runApp(const MyApp());
}
في هذا المثال قمنا بتحديد:
baseUrl: الرابط الأساسي للـ API.enableLogging: لتفعيل تسجيل الطلبات أثناء التطوير.timeout: لتحديد أقصى مدة انتظار للطلب.cacheConfig: لتفعيل التخزين المؤقت لمدة محددة.retryConfig: لإعادة محاولة الطلب عند الفشل.
مثال بدون استخدام flutter_api_helper
عند استخدام مكتبة HTTP عادية، قد تحتاج إلى كتابة كود أطول في كل مرة:
final response = await http.get(
Uri.parse('https://api.yourapp.com/products'),
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN',
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
print(data);
} else {
throw Exception('حدث خطأ أثناء جلب المنتجات');
}
هذا الأسلوب يعمل، لكنه يصبح متكررًا عند وجود عشرات الطلبات داخل التطبيق.
مثال باستخدام flutter_api_helper
باستخدام المكتبة، يمكن اختصار الطلب إلى شكل أبسط:
final products = await ApiHelper.get('products');
بدلًا من كتابة الرابط الكامل والرؤوس ومعالجة الأخطاء في كل مرة، تعتمد على الإعدادات التي قمت بتجهيزها مسبقًا.
مثال مع التخزين المؤقت وإعادة المحاولة
يمكنك تخصيص طلب معيّن ليستخدم التخزين المؤقت ويعيد المحاولة عند الفشل:
final products = await ApiHelper.get(
'products',
retryConfig: RetryConfig(
maxRetries: 3,
),
cacheConfig: CacheConfig(
duration: const Duration(minutes: 5),
),
);
هذا المثال مناسب لصفحات المنتجات أو القوائم التي لا تتغير كل ثانية، حيث يمكن حفظ الاستجابة مؤقتًا لمدة خمس دقائق، مع إعادة المحاولة ثلاث مرات إذا فشل الاتصال.
تنفيذ طلب POST لإرسال بيانات
عند الحاجة إلى إرسال بيانات مثل تسجيل مستخدم جديد أو إنشاء منتج، يمكنك استخدام طلب POST:
final response = await ApiHelper.post(
'users',
body: {
'name': 'Ahmed',
'email': 'ahmed@example.com',
'password': '12345678',
},
);
هذا النوع من الطلبات يُستخدم عادة في عمليات التسجيل، تسجيل الدخول، إرسال النماذج، أو إنشاء سجلات جديدة في قاعدة البيانات.
تنفيذ طلب PUT لتحديث البيانات
لتحديث بيانات موجودة مسبقًا، يمكن استخدام طلب PUT:
final response = await ApiHelper.put(
'users/15',
body: {
'name': 'Ahmed Albitar',
'email': 'ahmed.new@example.com',
},
);
في هذا المثال يتم تحديث بيانات المستخدم صاحب المعرّف 15.
تنفيذ طلب DELETE لحذف البيانات
لحذف عنصر من الخادم، يمكنك استخدام طلب DELETE:
final response = await ApiHelper.delete('products/10');
هذا المثال يحذف المنتج صاحب المعرّف 10 من خلال endpoint مخصص.
إضافة Authorization Token إلى الطلبات
في التطبيقات الحقيقية، تحتاج غالبًا إلى إرسال توكن مع الطلبات المحمية. يمكن عادةً ضبط التوكن ضمن الإعدادات العامة أو تمريره ضمن الرؤوس حسب طريقة عمل الإصدار الذي تستخدمه.
ApiHelper.configure(
ApiConfig(
baseUrl: 'https://api.yourapp.com',
defaultHeaders: {
'Accept': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
},
),
);
بهذه الطريقة يتم إرسال الرؤوس الافتراضية مع الطلبات، مما يقلل الحاجة إلى تكرارها في كل API call.
التعامل مع الأخطاء
من الأفضل دائمًا إحاطة طلبات الشبكة بـ try/catch حتى تتمكن من التعامل مع الأخطاء بشكل واضح داخل الواجهة.
try {
final products = await ApiHelper.get('products');
print(products);
} catch (error) {
print('حدث خطأ أثناء جلب البيانات: $error');
}
يمكنك لاحقًا تحويل الخطأ إلى رسالة مناسبة للمستخدم، مثل:
- تحقق من اتصال الإنترنت.
- انتهت صلاحية الجلسة، يرجى تسجيل الدخول مرة أخرى.
- الخادم غير متاح حاليًا.
- حدث خطأ غير متوقع.
استخدام flutter_api_helper داخل Service Class
للحفاظ على تنظيم المشروع، من الأفضل عدم كتابة طلبات API مباشرة داخل الواجهات. يمكنك إنشاء كلاس مخصص مثل ProductService:
class ProductService {
Future<dynamic> getProducts() async {
return await ApiHelper.get(
'products',
cacheConfig: CacheConfig(
duration: const Duration(minutes: 5),
),
);
}
Future<dynamic> getProductDetails(int id) async {
return await ApiHelper.get('products/$id');
}
Future<dynamic> createProduct(Map<String, dynamic> data) async {
return await ApiHelper.post(
'products',
body: data,
);
}
Future<dynamic> deleteProduct(int id) async {
return await ApiHelper.delete('products/$id');
}
}
بهذا الشكل تصبح الواجهة مسؤولة فقط عن عرض البيانات، بينما تكون طبقة الخدمة مسؤولة عن الاتصال بالخادم.
مثال استخدام داخل واجهة Flutter
يمكنك استخدام الخدمة السابقة داخل Widget لعرض قائمة المنتجات:
class ProductsPage extends StatefulWidget {
const ProductsPage({super.key});
@override
State<ProductsPage> createState() => _ProductsPageState();
}
class _ProductsPageState extends State<ProductsPage> {
final ProductService _productService = ProductService();
bool isLoading = true;
dynamic products;
String? errorMessage;
@override
void initState() {
super.initState();
loadProducts();
}
Future<void> loadProducts() async {
try {
final result = await _productService.getProducts();
setState(() {
products = result;
isLoading = false;
});
} catch (error) {
setState(() {
errorMessage = 'تعذر تحميل المنتجات';
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (errorMessage != null) {
return Center(
child: Text(errorMessage!),
);
}
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product['name'].toString()),
subtitle: Text(product['price'].toString()),
);
},
);
}
}
هذا المثال يوضّح كيف يمكن فصل منطق الشبكة عن الواجهة، مما يجعل الكود أسهل في القراءة والصيانة.
متى تستخدم التخزين المؤقت؟
التخزين المؤقت مفيد عندما تكون البيانات لا تتغير بسرعة، أو عندما تريد تقليل عدد الطلبات المرسلة إلى الخادم.
يمكن استخدامه في حالات مثل:
- قائمة المنتجات.
- التصنيفات.
- الإعدادات العامة.
- بيانات الصفحة الرئيسية.
- المحتوى الذي لا يحتاج إلى تحديث لحظي.
لكن لا يُفضّل استخدام التخزين المؤقت مع البيانات الحساسة أو المتغيرة بسرعة، مثل الرصيد المالي، حالة الطلب الحالية، أو بيانات الدفع.
متى تستخدم إعادة المحاولة؟
ميزة إعادة المحاولة مفيدة عند وجود أخطاء مؤقتة في الاتصال أو الخادم. على سبيل المثال، إذا فشل الطلب بسبب ضعف الشبكة، يمكن إعادة المحاولة تلقائيًا بدلًا من إظهار الخطأ فورًا للمستخدم.
final data = await ApiHelper.get(
'orders',
retryConfig: RetryConfig(
maxRetries: 3,
),
);
لكن يجب استخدامها بحذر، لأن إعادة المحاولة الكثيرة قد تزيد الضغط على الخادم أو تؤخر ظهور الخطأ للمستخدم.
نصائح مهمة عند استخدام flutter_api_helper
- اجعل إعدادات API في مكان مركزي داخل المشروع.
- لا تكتب طلبات الشبكة مباشرة داخل كل Widget.
- استخدم Service Classes لتنظيم الطلبات حسب كل جزء من التطبيق.
- فعّل تسجيل الطلبات في بيئة التطوير فقط.
- لا تطبع التوكنات أو البيانات الحساسة في السجلات.
- استخدم التخزين المؤقت فقط مع البيانات المناسبة.
- تعامل مع أخطاء الشبكة برسائل واضحة للمستخدم.
- راجع توثيق الحزمة عند كل تحديث للتأكد من أسماء الدوال والمعاملات.
مميزات مكتبة flutter_api_helper
- تبسيط التعامل مع REST APIs.
- تقليل الكود المتكرر في المشروع.
- دعم إعدادات عامة مثل
baseUrlوtimeout. - إمكانية إدارة الرؤوس والتوكنات.
- دعم تسجيل الطلبات أثناء التطوير.
- دعم التخزين المؤقت لتحسين الأداء.
- دعم إعادة المحاولة عند فشل الطلبات.
- تحسين تنظيم طبقة الشبكة داخل التطبيق.
عيوب أو ملاحظات قبل الاعتماد عليها
- قد تختلف أسماء الدوال أو الإعدادات بين الإصدارات.
- الحزمة قد لا تكون مناسبة إذا كنت تحتاج إلى تحكم منخفض المستوى جدًا في كل طلب.
- يجب اختبار سلوك التخزين المؤقت جيدًا حتى لا تعرض بيانات قديمة للمستخدم.
- يجب التعامل بحذر مع تسجيل الطلبات حتى لا تظهر بيانات حساسة في السجلات.
- يفضّل مراجعة نشاط الحزمة وتحديثاتها قبل استخدامها في مشروع إنتاجي كبير.
هل flutter_api_helper مناسبة لمشروعك؟
إذا كان تطبيقك يعتمد على REST APIs بشكل متكرر وتريد تقليل التكرار وتنظيم طبقة الشبكة، فإن flutter_api_helper يمكن أن تكون خيارًا مفيدًا. فهي تساعدك على تنفيذ الطلبات بسرعة، إدارة الإعدادات العامة، وتوحيد التعامل مع الأخطاء.
أما إذا كان مشروعك يحتاج إلى تحكم متقدم جدًا في الشبكة، مثل Interceptors معقدة، Cancel Tokens، Upload/Download Progress بتفاصيل دقيقة، أو تكاملات كبيرة، فقد تحتاج إلى مقارنة المكتبة مع حلول أخرى مثل dio أو بناء طبقة API مخصصة.
الخلاصة
تساعد مكتبة flutter_api_helper على تبسيط التعامل مع REST APIs داخل تطبيقات Flutter، من خلال توفير طريقة أسهل لإرسال الطلبات، إدارة الإعدادات، التعامل مع الأخطاء، تفعيل التخزين المؤقت، وإعادة المحاولة عند فشل الاتصال.
الميزة الأهم في المكتبة أنها تقلل التكرار وتجعل كود الشبكة أكثر تنظيمًا، خاصة في التطبيقات التي تحتوي على عدد كبير من الطلبات. ومع ذلك، يجب دائمًا مراجعة التوثيق الرسمي للحزمة واختبار الأداء وسلوك الأخطاء قبل الاعتماد عليها في الإنتاج.
إذا كنت تريد إنشاء طبقة API بسيطة وسريعة داخل تطبيق Flutter، فإن flutter_api_helper تستحق التجربة، خصوصًا في المشاريع الصغيرة والمتوسطة أو التطبيقات التي تحتاج إلى تنظيم سريع لطلبات الشبكة.
المصادر
مزيد من المقالات : شرح Flutter Toast Message : تغيير شكل Toast Message وإضافة Animation باحترافية




