import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:second_app/contact.dart';
import 'package:second_app/products.dart';
import 'colors.dart';
import 'package:http/http.dart';
class HomaPage extends StatefulWidget {
HomaPage({super.key});
@override
State<HomaPage> createState() => _HomaPageState();
}
class _HomaPageState extends State<HomaPage> {
final userNameController = TextEditingController();
final formKey = GlobalKey<FormState>();
String? userName;
List data = [];
bool loading = true;
getData() async {
var response = await get(Uri.parse("https://fakestoreapi.com/products"));
var resBody = jsonDecode(response.body);
data.clear(); // عشان ما يتكرر التحميل
data.addAll(resBody);
loading = false;
// setState(() {});
}
@override
void initState() {
getData();
super.initState();
}
@override
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Fake Store")),
drawer: Drawer(),
body: Column(
children: [
if (loading) Center(child: Text("جاري التحميل")),
Expanded(
child: GridView.builder(
padding: EdgeInsets.all(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // عدد الأعمدة
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 0.7,
),
itemCount: data.length,
itemBuilder: (context, i) {
final item = data[i];
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// الصورة
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.vertical(
top: Radius.circular(8),
),
child: Image.network(
item['image'],
width: double.infinity,
fit: BoxFit.cover,
),
),
),
// النصوص
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['title'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
Text(
item['description'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(),
),
SizedBox(height: 4),
Text(
"\$${item['price']}",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
);
},
),
),
],
),
);
}
}
Dartشرح الدالة getData()
getData() async {Dart- async: تعني أن الدالة غير متزامنة وتُرجِع
Future. - هذا يسمح لك باستخدام
awaitبداخلها لتنفيذ عمليات تستغرق وقتًا (مثل طلبات الشبكة) بدون تجميد واجهة المستخدم.
var response = await get(Uri.parse("https://fakestoreapi.com/products"));Dart- Uri.parse(…): يحوّل النص إلى كائن
Uriصالح لطلب HTTP. - get(…): استدعاء HTTP GET (عادة من حزمة
http). - await: يوقف تنفيذ الدالة هنا حتى يعود الرد من الخادم.
- response: يحتوي على كود الحالة (
statusCode) والرأس (headers) والنص (body) للرد.
var resBody = jsonDecode(response.body);Dart- jsonDecode: يفك ترميز الـ JSON النصي إلى كائنات Dart (غالبًا
List<dynamic>أوMap<String, dynamic>حسب البيانات). - هنا الـ API يُرجع قائمة منتجات، فـ
resBodyستكون غالبًاList<dynamic>من العناصر (كل عنصرMap<String, dynamic>).
data.clear(); // عشان ما يتكرر التحميلDart- يفّضي القائمة
dataقبل إضافة البيانات الجديدة لتجنّب التكرار في كل مرّة تُستدعى فيها الدالة.
data.addAll(resBody);Dart- يضيف كل عناصر
resBodyإلىdataدفعة واحدة. - مهم: تأكد أن نوع
dataمتوافق (مثلاًList<Map<String, dynamic>>أوList<dynamic>).
loading = false;Dart- يحدّث فلاغ التحميل ليشير إلى أن البيانات أصبحت جاهزة.
setState(() {});Dartدورة حياة الـ Widget — initState
@override
void initState() {
super.initState();
getData();
}Dart- initState: تُستدعى مرّة واحدة عند إنشاء حالة الـ
StatefulWidget. - المكان الصحيح لبدء تحميل البيانات الأولية.
- ملاحظة مهمة: من الأفضل استدعاء
super.initState()أولاً (وإن كانت تعمل غالبًا لو أخّرتها، لكن تنصيح Flutter الرسمي يضعها في البداية). - استدعاء
getData()بدونawaitهنا صحيح؛ لأنinitStateليست async. - الدالة ستبدأ التحميل في الخلفية، وحين تنتهي ينبغي أن تُحدّث الحالة (عادةً عبر
setState).
👇 التوضيح بالتفصيل:
🔹 الكلاس Uri
- هو كلاس مدمج في Dart (وليس Flutter ولا http).
- يُستخدم لإنشاء وتحليل روابط الإنترنت (Uniform Resource Identifier).
- مثلاً:
var uri = Uri.parse("https://example.com/products?id=1"); print(uri.scheme); // https print(uri.host); // example.com print(uri.path); // /products print(uri.query); // id=1
🔹 الدالة Uri.parse(String url)
- تحوّل النص (String) إلى كائن من نوع
Uri. - هذا الكائن يُستخدم بواسطة مكتبة
httpأو أي مكتبة أخرى لإرسال الطلبات.
🔹 مكتبة http
- مكتبة خارجية (من pub.dev).
- الدالة
get(...)الخاصة بها تتوقّع كائنًا من نوعUriكمعامل. - لذلك تكتب:
var response = await get(Uri.parse("https://fakestoreapi.com/products")); - وليس:
var response = await get("https://fakestoreapi.com/products"); // ❌ خطألأنget()يريدUriوليسString.
📘 خلاصة:
| الكود | ينتمي إلى | وظيفته |
|---|---|---|
Uri.parse(...) | مكتبة Dart الأساسية (dart:core) | تحويل النص إلى كائن URI |
get() | مكتبة http (من pub.dev) | إرسال طلب HTTP GET باستخدام الـ URI |
🔹 السطر:
var resBody = jsonDecode(response.body);Dart📍 المعنى العام:
يفك ترميز (decode) النص بصيغة JSON القادم من الخادم (الـ API) ويحوّله إلى كائنات Dart (قوائم أو خرائط).
📦 تفصيل كل جزء:
1️⃣ response
- هذا هو الكائن الذي رجع من السطر:
var response = await get(Uri.parse("https://fakestoreapi.com/products")); - يحتوي على بيانات الرد من الخادم.
- أهم الخصائص فيه:
response.statusCode→ رقم حالة الطلب (200 = ناجح، 404 = غير موجود…)response.body→ النص الكامل الذي أرسله السيرفر (عادة يكون بصيغة JSON).
2️⃣ response.body
- هي نص (String) يحتوي على البيانات الخام من الخادم.
- مثال فعلي من
https://fakestoreapi.com/products:[ { "id": 1, "title": "Fjallraven - Foldsack No. 1 Backpack", "price": 109.95, "category": "men's clothing", "image": "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg" }, ... ] - هذا مجرد نص JSON، ليس كائنًا يمكن التعامل معه مباشرة في Dart.
3️⃣ jsonDecode(...)
- دالة من مكتبة
dart:convert(مكتبة رسمية من Dart). - تُحوِّل النص JSON إلى كائنات Dart:
- إذا كان JSON قائمة (
[...]) → تتحول إلىList<dynamic> - إذا كان JSON كائنًا (
{...}) → يتحول إلىMap<String, dynamic>
- إذا كان JSON قائمة (
📦 لذلك:
import 'dart:convert';Dartيجب أن يكون موجودًا في أعلى الملف.
4️⃣ النتيجة:
في مثالنا، السيرفر يُرجع قائمة من المنتجات، لذا:
var resBody = jsonDecode(response.body);Dartينتج:
resBody is List<dynamic>;
resBody[0] is Map<String, dynamic>;Dartأي:
resBody= قائمة فيها عناصر (كل عنصر منتج).- كل منتج عبارة عن خريطة (
Map) تحتوي على مفاتيح وقيم مثل"title","price","image", إلخ.
🔹 مثال عملي:
var response = await get(Uri.parse("https://fakestoreapi.com/products"));
var resBody = jsonDecode(response.body);
print(resBody.runtimeType); // List<dynamic>
print(resBody[0]['title']); // يطبع عنوان أول منتج
print(resBody[0]['price']); // يطبع السعر
Dart⚠️ ملاحظات مهمة:
- تأكّد من نجاح الطلب قبل محاولة jsonDecode
if (response.statusCode == 200) {
var resBody = jsonDecode(response.body);
} else {
print('حدث خطأ في التحميل: ${response.statusCode}');
}Dart- لو حاولت تفكيك نص ليس JSON صالح، سترمي
jsonDecodeخطأ (FormatException). - استخدم أنواع صريحة إذا عرفت نوع البيانات:
List<Map<String, dynamic>> resBody = List<Map<String, dynamic>>.from(jsonDecode(response.body));Dart🧠 خلاصة الجدول:
| العنصر | من أين؟ | نوعه | وظيفته |
|---|---|---|---|
response | من مكتبة http | Response | نتيجة الطلب من السيرفر |
response.body | خاصية في Response | String | النص (عادة JSON) من الخادم |
jsonDecode() | من مكتبة dart:convert | Function | تحويل JSON النصي إلى كائنات Dart |
resBody | متغيرك | List أو Map | البيانات الجاهزة للاستخدام في الكود |