خلّينا نعملها كـ ⭐⭐⭐⭐⭐ تقدر تغيّرها بالضغط، وبنفس الوقت تقدر لاحقًا تربطها بقاعدة بيانات.
1️⃣ منطق التقييم (ببساطة)
- نحتاج متغيّر في الـ
Stateيخزن قيمة التقييم الحالي بين 0 و 5. - نعرض صف (
Row) فيه 5 أيقونات نجوم. - لكل نجمة:
- لو رقمها أقل أو يساوي قيمة التقييم → نعرض
Icons.star. - لو أكبر → نعرض
Icons.star_border.
- لو رقمها أقل أو يساوي قيمة التقييم → نعرض
- لما المستخدم يضغط على نجمة رقم 3 مثلًا → نخزّن 3 في المتغيّر ونعيد بناء الواجهة (
setState).
2️⃣ إضافة متغيّر للتقييم في صفحة التفاصيل
داخل _ProductDetailsPageState:
class _ProductDetailsPageState extends State<ProductDetailsPage> {
int _selectedColorIndex = 0;
int _selectedSizeIndex = 0;
int _quantity = 1;
// ⭐ الكود المضاف: التقييم (من 0 إلى 5)
int _rating = 0;JavaScriptلاحقًا لو عندك تقييم جاهز في الـ
ProductModelتقدر تبدأه بـ:JavaScriptint _rating = widget.productDetails.rating?.round() ?? 0;في
initState، لكن نخليها بسيطة الآن.
3️⃣ ودجت لعرض النجوم والتفاعل معها
أضِف هذه الدالة داخل نفس الكلاس:
Widget _buildRatingStars() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(5, (index) {
final starIndex = index + 1; // عشان يصير 1..5 بدل 0..4
return IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () {
setState(() {
_rating = starIndex; // مثال: ضغط على النجمة الثالثة ⇒ التقييم 3
});
},
icon: Icon(
_rating >= starIndex ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 28,
),
);
}),
);
}JavaScriptهذا يعطينا 5 نجوم قابلة للضغط، وكل ما تضغط تتغيّر كل النجوم قبلها لممتلئة ⭐.
4️⃣ استدعاء ودجت النجوم في صفحة التفاصيل
في build، داخل الـ Column، مكان مناسب مثلاً تحت عنوان المنتج:
Text(
widget.productDetails.title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
// ⭐⭐ عرض النجوم هنا
_buildRatingStars(),
const SizedBox(height: 8),
Text(
'التقييم: $_rating من 5',
style: const TextStyle(fontSize: 14, color: Colors.grey),
),JavaScript5️⃣ طيب لو أبغاه “قراءة فقط” (من البيانات بدون ضغط)؟
سهل، تشيل onPressed أو تخليه null وتستخدم نفس المنطق:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(5, (index) {
final starIndex = index + 1;
return Icon(
widget.productDetails.rating >= starIndex ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 20,
);
}),
);JavaScriptلو تحب في الخطوة الجاية:
- أضيف لك حقل
ratingللـProductModel - وأخلي صفحة التفاصيل تستقبل تقييم من الـ data بدلاً من أن يكون فقط محلي في الصفحة 💪
You said:
اشرح لي الموضوع بالتفصيل سطر سطر مه كل خاصية وقيمة
ChatGPT said:
تمام، خلّينا نفكّك الموضوع على مهلك، سطر سطر 🌟
سأشرح 3 أجزاء:
- متغيّر التقييم
_rating - ويدجت النجوم
_buildRatingStars - كيف نستخدمها داخل واجهة التفاصيل
1️⃣ متغيّر التقييم
int _rating = 0;JavaScriptint
يعني نوع المتغيّر هو عدد صحيح (0، 1، 2، 3، 4، 5)._rating
اسم المتغيّر، وهو يخزّن التقييم الحالي لرقم النجوم.- يبدأ بـ 0 → يعني لا يوجد تقييم بعد.
- لو صار 3 → يعني المستخدم اختار 3 نجوم.
= 0;
القيمة الابتدائية (الافتراضية) عند فتح الصفحة.
هذا المتغيّر موجود داخل
Stateعشان إذا تغيّر ونديناsetState، الواجهة تعيد البناء وتُظهر عدد النجوم الجديدة.
2️⃣ ويدجت النجوم: _buildRatingStars
الكود:
Widget _buildRatingStars() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(5, (index) {
final starIndex = index + 1;
return IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
onPressed: () {
setState(() {
_rating = starIndex;
});
},
icon: Icon(
_rating >= starIndex ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 28,
),
);
}),
);
}JavaScript🔹 السطر الأول
Widget _buildRatingStars() {
JavaScriptWidget
نوع الدالة → ترجع ويدجت يمكن عرضها في الواجهة._buildRatingStars
اسم الدالة. بدأناها بـ_يعني خاصة داخل هذا الكلاس فقط.()
ما تستقبل أي باراميترات هنا.{
بداية جسم الدالة.
🔹 السطر الثاني
return Row(
JavaScriptreturn
الدالة سترجع هذا الـ Widget.Row(
نستخدمRowلعرض النجوم جنب بعض في صف أفقي.
🔹 خاصية mainAxisAlignment
mainAxisAlignment: MainAxisAlignment.center,
JavaScriptmainAxisAlignment:
كيف نوزّع العناصر على المحور الرئيسي (فيRow→ أفقي).MainAxisAlignment.center
يضع النجوم في منتصف الصف أفقيًا.
🔹 خاصية children
children: List.generate(5, (index) {JavaScriptchildren:
هذه هي قائمة العناصر (النجوم) داخل الـ Row.List.generate(5, (index) { ... })
تنشئ قائمة تتكون من 5 عناصر (نجوم)، من 0 إلى 4.5→ عدد النجوم.(index)→ رقم العنصر الحالي (0، 1، 2، 3، 4).- كل مرة تستدعي الدالة المجهولة
{ ... }لبناء نجمة واحدة.
🔹 تحديد رقم النجمة (1–5)
final starIndex = index + 1;JavaScriptfinal
قيمة ثابتة لا تتغير خلال هذا البناء.starIndex
اسم المتغيّر. يمثّل رقم النجمة بالنسبة للمستخدم.index + 1
لأنindexيبدأ من 0، نضيف 1 ليصبح:- لو
index = 0→starIndex = 1 index = 1→starIndex = 2- … حتى 5.
- لو
هذا يجعل التقييم منطقي: نجمة 1، نجمتين، 3 نجوم… بدل 0، 1، 2…
🔹 بناء زر النجمة
return IconButton(<br>JavaScriptreturn
نُرجع ويدجت واحدة تمثل نجمة واحدة.IconButton(
ويدجت جاهزة عبارة عن زر يحتوي أيقونة، ويمكن الضغط عليها.
🔹 خاصية padding
padding: EdgeInsets.zero,
JavaScriptpadding:
المسافة الداخلية حول الأيقونة داخل الزر.EdgeInsets.zero
يعني لا يوجد padding → الزر حول الأيقونة مباشرة، ليس كبيرًا.
🔹 خاصية constraints
constraints: const BoxConstraints(),
JavaScriptconstraints:
قيود الحجم للـIconButton.BoxConstraints()
هنا نستخدم إعدادات افتراضية بسيطة.const
لأن القيمة ثابتة لا تتغير.
الهدف هنا نحافظ على حجم الزر بسيط وما يأخذ مساحة إضافية.
🔹 عند الضغط على النجمة
onPressed: () {
setState(() {
_rating = starIndex;
});
},
JavaScriptonPressed:
دالة تُستدعى عندما يضغط المستخدم على النجمة.() { ... }
دالة بدون باراميترات.
داخلها:
setState(() { ... });
تخبر Flutter أن هناك تغيير في البيانات (state)، فيعيد بناء الواجهة.
داخل setState:
_rating = starIndex;
JavaScript- نضع التقييم الحالي
_ratingمساويًا لـstarIndex.- لو المستخدم ضغط النجمة 4 →
starIndex = 4→_rating = 4. - المرة القادمة عند بناء النجوم نستخدم القيمة الجديدة.
- لو المستخدم ضغط النجمة 4 →
هذا هو الجزء اللي يغيّر التقييم فعليًا ويحدّث واجهة النجوم.
🔹 الأيقونة نفسها (شكل النجمة)
icon: Icon(
_rating >= starIndex ? Icons.star : Icons.star_border,
color: Colors.amber,
size: 28,
),
JavaScripticon:
الأيقونة التي سيعرضها الـIconButton.Icon(
ويدجت الأيقونة نفسها.
داخل Icon:
1) نوع الأيقونة (ممتلئة أو فارغة):
_rating >= starIndex ? Icons.star : Icons.star_border,
JavaScript- هذا اسمه ternary operator (شرط في سطر واحد):
ترجمته:
إذا كان
_rating >= starIndex
→ استخدمIcons.star(نجمة ممتلئة)
وإلا
→ استخدمIcons.star_border(نجمة فارغة)
مثال:
_rating = 3- النجمة1 (
starIndex = 1) → 3 >= 1 → ✅ ممتلئة - النجمة2 (
starIndex = 2) → 3 >= 2 → ✅ ممتلئة - النجمة3 (
starIndex = 3) → 3 >= 3 → ✅ ممتلئة - النجمة4 (
starIndex = 4) → 3 >= 4 → ❌ فارغة - النجمة5 (
starIndex = 5) → 3 >= 5 → ❌ فارغة
- النجمة1 (
2) لون الأيقونة:
color: Colors.amber,
JavaScriptcolor:
لون الأيقونة نفسها.Colors.amber
لون أصفر ذهبي مناسب للنجوم.
3) حجم الأيقونة:
size: 28,
JavaScriptsize:
حجم الأيقونة بالبكسل.28
يعني حجم متوسط/كبير قليلًا، تقدر تغيّره لو حاب.
🔹 إغلاق الـ IconButton و List.generate و Row
);
}),
);
}
JavaScript);الأول → إغلاقIconButton.}),→ إغلاق الدالة داخلList.generate.);→ إغلاق الـRow.}→ نهاية جسم الدالة_buildRatingStars.
3️⃣ استخدام النجوم في صفحة التفاصيل
مثال:
Text(
widget.productDetails.title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
_buildRatingStars(), // 👈 هنا تظهر النجوم
const SizedBox(height: 8),
Text(
'التقييم: $_rating من 5',
style: const TextStyle(fontSize: 14, color: Colors.grey),
),JavaScript- نعرض عنوان المنتج.
- بعده ننادي
_buildRatingStars()لعرض النجوم. - بعده نعرض نص يوضح التقييم الحالي المخزّن في
_rating.