تقييم النجوم للمنتج

خلّينا نعملها كـ ⭐⭐⭐⭐⭐ تقدر تغيّرها بالضغط، وبنفس الوقت تقدر لاحقًا تربطها بقاعدة بيانات.

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 تقدر تبدأه بـ:

int _rating = widget.productDetails.rating?.round() ?? 0;
JavaScript

في 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),
),
JavaScript
5️⃣ طيب لو أبغاه “قراءة فقط” (من البيانات بدون ضغط)؟

سهل، تشيل 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 أجزاء:

  1. متغيّر التقييم _rating
  2. ويدجت النجوم _buildRatingStars
  3. كيف نستخدمها داخل واجهة التفاصيل
1️⃣ متغيّر التقييم
int _rating = 0;
JavaScript
  • int
    يعني نوع المتغيّر هو عدد صحيح (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() {
JavaScript
  • Widget
    نوع الدالة → ترجع ويدجت يمكن عرضها في الواجهة.
  • _buildRatingStars
    اسم الدالة. بدأناها بـ _ يعني خاصة داخل هذا الكلاس فقط.
  • ()
    ما تستقبل أي باراميترات هنا.
  • {
    بداية جسم الدالة.

🔹 السطر الثاني

  return Row(
JavaScript
  • return
    الدالة سترجع هذا الـ Widget.
  • Row(
    نستخدم Row لعرض النجوم جنب بعض في صف أفقي.
🔹 خاصية mainAxisAlignment
    mainAxisAlignment: MainAxisAlignment.center,
JavaScript
  • mainAxisAlignment:
    كيف نوزّع العناصر على المحور الرئيسي (في Row → أفقي).
  • MainAxisAlignment.center
    يضع النجوم في منتصف الصف أفقيًا.
🔹 خاصية children
    children: List.generate(5, (index) {
JavaScript
  • children:
    هذه هي قائمة العناصر (النجوم) داخل الـ Row.
  • List.generate(5, (index) { ... })
    تنشئ قائمة تتكون من 5 عناصر (نجوم)، من 0 إلى 4.
    • 5 → عدد النجوم.
    • (index) → رقم العنصر الحالي (0، 1، 2، 3، 4).
    • كل مرة تستدعي الدالة المجهولة { ... } لبناء نجمة واحدة.
🔹 تحديد رقم النجمة (1–5)
      final starIndex = index + 1;
JavaScript
  • final
    قيمة ثابتة لا تتغير خلال هذا البناء.
  • starIndex
    اسم المتغيّر. يمثّل رقم النجمة بالنسبة للمستخدم.
  • index + 1
    لأن index يبدأ من 0، نضيف 1 ليصبح:
    • لو index = 0starIndex = 1
    • index = 1starIndex = 2
    • … حتى 5.

هذا يجعل التقييم منطقي: نجمة 1، نجمتين، 3 نجوم… بدل 0، 1، 2…

🔹 بناء زر النجمة
      return IconButton(<br>
JavaScript
  • return
    نُرجع ويدجت واحدة تمثل نجمة واحدة.
  • IconButton(
    ويدجت جاهزة عبارة عن زر يحتوي أيقونة، ويمكن الضغط عليها.

🔹 خاصية padding

        padding: EdgeInsets.zero,
JavaScript
  • padding:
    المسافة الداخلية حول الأيقونة داخل الزر.
  • EdgeInsets.zero
    يعني لا يوجد padding → الزر حول الأيقونة مباشرة، ليس كبيرًا.

🔹 خاصية constraints

        constraints: const BoxConstraints(),
JavaScript
  • constraints:
    قيود الحجم للـ IconButton.
  • BoxConstraints()
    هنا نستخدم إعدادات افتراضية بسيطة.
  • const
    لأن القيمة ثابتة لا تتغير.

الهدف هنا نحافظ على حجم الزر بسيط وما يأخذ مساحة إضافية.

🔹 عند الضغط على النجمة

        onPressed: () {
          setState(() {
            _rating = starIndex;
          });
        },
JavaScript
  • onPressed:
    دالة تُستدعى عندما يضغط المستخدم على النجمة.
  • () { ... }
    دالة بدون باراميترات.

داخلها:

  • setState(() { ... });
    تخبر Flutter أن هناك تغيير في البيانات (state)، فيعيد بناء الواجهة.

داخل setState:

_rating = starIndex;
JavaScript
  • نضع التقييم الحالي _rating مساويًا لـ starIndex.
    • لو المستخدم ضغط النجمة 4 → starIndex = 4_rating = 4.
    • المرة القادمة عند بناء النجوم نستخدم القيمة الجديدة.

هذا هو الجزء اللي يغيّر التقييم فعليًا ويحدّث واجهة النجوم.

🔹 الأيقونة نفسها (شكل النجمة)
        icon: Icon(
          _rating >= starIndex ? Icons.star : Icons.star_border,
          color: Colors.amber,
          size: 28,
        ),
JavaScript
  • icon:
    الأيقونة التي سيعرضها الـ 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 → ❌ فارغة

2) لون الأيقونة:

          color: Colors.amber,
JavaScript
  • color:
    لون الأيقونة نفسها.
  • Colors.amber
    لون أصفر ذهبي مناسب للنجوم.

3) حجم الأيقونة:

          size: 28,
JavaScript
  • size:
    حجم الأيقونة بالبكسل.
  • 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.