إرسال طلبات GET وتحليل استجابات JSON
إرسال طلبات GET وتحليل استجابات JSON
تحتاج كل تطبيقات Flutter العملية تقريباً إلى التواصل مع خادم بعيد. العملية الأكثر شيوعاً هي طلب GET — طلب البيانات من الخادم — تليها عملية التحليل لتحويل تلك البيانات من JSON إلى كائنات Dart. في هذا الدرس ستتعلم سير العمل الكامل: إضافة حزمة http، إرسال طلب GET، فحص الاستجابة، وتحويل سلسلة JSON الخام إلى خرائط وقوائم Dart قابلة للاستخدام باستخدام dart:convert.
حزمة http
لا يأتي Flutter مع عميل HTTP مدمج (يوجد HttpClient في dart:io لكنه مطوّل). الحل المعياري في المجتمع هو حزمة http المنشورة من قِبل فريق Dart. أضفها إلى pubspec.yaml:
إضافة التبعية في pubspec.yaml
dependencies:
flutter:
sdk: flutter
http: ^1.2.1 # تحقق دائماً من أحدث إصدار على pub.dev
نفّذ flutter pub get لتنزيلها. ثم استورد كلتا المكتبتين في أعلى ملف Dart الذي سينفذ الطلب:
الاستيرادات المطلوبة
import 'package:http/http.dart' as http;
import 'dart:convert';
http باستخدام اللقب as http هو الأسلوب المعتاد. يمنع تعارض الأسماء ويجعل من الواضح فوراً أن http.get() تأتي من الحزمة وليس من كودك.تنفيذ طلب GET
http.get() دالة غير متزامنة (async) تُرجع Future<http.Response>. يجب أن تنتظرها بـ await داخل دالة async. المصنع Uri.parse() يحوّل سلسلة URL العادية إلى نوع Uri الذي تتوقعه الحزمة.
طلب GET أساسي
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<void> fetchUsers() async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/users');
final response = await http.get(uri);
if (response.statusCode == 200) {
// نجاح — الجسم سلسلة JSON
print('نص الاستجابة: ${response.body}');
} else {
// غير 2xx يعني حدث خطأ ما
throw Exception('فشل تحميل المستخدمين: ${response.statusCode}');
}
}
كائن http.Response يكشف عدة أعضاء مفيدة:
response.statusCode— رمز حالة HTTP (200، 404، 500، إلخ)response.body— جسم الاستجابة كـStringresponse.headers— خريطةMap<String, String>لترويسات الاستجابةresponse.bodyBytes— الجسم الخام كـUint8List(مفيد للبيانات الثنائية)
تحليل JSON باستخدام dart:convert
مكتبة dart:convert توفر الدالة العلوية jsonDecode(). تقبل سلسلة JSON وتُرجع قيمة dynamic في Dart — إما Map<String, dynamic> (لكائن JSON) أو List<dynamic> (لمصفوفة JSON). ثم تُحوّل وتصل إلى الحقول بالاسم.
تحليل مصفوفة JSON من الكائنات
Future<List<Map<String, dynamic>>> fetchPosts() async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await http.get(uri);
if (response.statusCode != 200) {
throw Exception('HTTP ${response.statusCode}');
}
// jsonDecode يُرجع dynamic — حوّله إلى الشكل المتوقع
final List<dynamic> jsonList = jsonDecode(response.body) as List<dynamic>;
// تحويل كل عنصر إلى Map مُحدد النوع
final posts = jsonList
.map((item) => item as Map<String, dynamic>)
.toList();
for (final post in posts) {
print('العنوان: ${post['title']}');
print('الجسم: ${post['body']}');
print('معرف المستخدم: ${post['userId']}');
print('---');
}
return posts;
}
fromJson. هذه الخطوة مغطاة في الدرس التالي. في الوقت الحالي، العمل مع الخرائط الخام يتيح لك فهم ما تُرجعه jsonDecode فعلياً قبل إضافة طبقة تجريد إضافية.تحليل كائن JSON واحد
عندما يُرجع الخادم كائن JSON واحداً (وليس مصفوفة)، تُرجع jsonDecode Map<String, dynamic>. استخدم بناء جملة القوسين المألوف للوصول إلى الحقول:
تحليل كائن JSON
Future<Map<String, dynamic>> fetchUser(int id) async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/users/$id');
final response = await http.get(uri);
if (response.statusCode != 200) {
throw Exception('المستخدم غير موجود (${response.statusCode})');
}
final Map<String, dynamic> user =
jsonDecode(response.body) as Map<String, dynamic>;
// الوصول إلى الحقول الفردية
final String name = user['name'] as String;
final String email = user['email'] as String;
final Map<String, dynamic> address =
user['address'] as Map<String, dynamic>;
final String city = address['city'] as String;
print('الاسم: $name، البريد: $email، المدينة: $city');
return user;
}
jsonDecode() قيمة dynamic. إذا وصلت إلى حقل غير موجود في JSON، يُرجع Dart القيمة null وقت التشغيل — لا يُطلق خطأ وقت الترجمة. تحقق دائماً من null أو استخدم ?.cast<>() عندما قد يكون الحقل غائباً، خاصة مع حقول API الاختيارية أو القابلة للـ null.معالجة الأخطاء والاستثناءات الشبكية
قد تفشل مكالمات الشبكة لأسباب تتجاوز رمز الحالة السيئ: لا إنترنت، فشل DNS، انتهاء مهلة الخادم، أو سلسلة JSON مشوهة. لف طلبك في try-catch للتعامل بأناقة مع أخطاء HTTP واستثناءات المقبس:
جلب قوي مع معالجة الأخطاء
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:io'; // لـ SocketException
Future<List<dynamic>> safeFetchPosts() async {
try {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'))
.timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
return jsonDecode(response.body) as List<dynamic>;
} else {
throw Exception('خطأ الخادم: ${response.statusCode}');
}
} on SocketException {
throw Exception('لا يوجد اتصال بالإنترنت');
} on FormatException {
throw Exception('تنسيق استجابة خاطئ — تعذر تحليل JSON');
}
}
ملخص
لإرسال طلب GET وتحليل JSON في Flutter: أضف http إلى التبعيات، استورد http وdart:convert، استدعِ await http.get(Uri.parse(url))، تحقق من response.statusCode == 200، ومرر response.body إلى jsonDecode(). النتيجة إما Map<String, dynamic> أو List<dynamic> يمكنك التكرار عليها أو تحويلها. لف الاستدعاء في try-catch للتعامل مع مشاكل الاتصال والاستجابات المشوهة.
http.get() مع jsonDecode() يشكلان أساس كل عمليات الشبكة في Flutter. كل نمط ذي مستوى أعلى — فئات النماذج، المستودعات، تكامل FutureBuilder — يُبنى فوق هذين الأساسيين.