Flexible

أولاً: Flexible
ما هو؟

Flexible ويدجت تُستخدم داخل Row/Column/Flex لتوزيع المساحة المتبقية بمرونة.
على عكس Expanded (الذي يُجبر الطفل على ملء حصته بالكامل)، فإن Flexible يمكن أن:

  • يسمح للطفل بأخذ ما يحتاجه فقط من حصته (الوضع الافتراضي loose).
  • أو يجبره على ملء حصته بالكامل إذا عيّنت fit: FlexFit.tight.

باختصار:
Expanded = Flexible(fit: FlexFit.tight)
بينما Flexible (بدون fit) = مرن ومُتساهل (loose).

الصيغة
Flexible({
  Key? key,
  int flex = 1,
  FlexFit fit = FlexFit.loose, // الافتراضي
  required Widget child,
})
Dart
كيف يوزّع المساحة؟
  • يحجز الـ Row/Column العناصر غير المرنة أولاً.
  • يوزّع الباقي على العناصر المرنة حسب قيمة flex.
  • مع Flexible(loose) يُعطي للطفل قيودًا فضفاضة (يمكنه أن يكون أصغر من حصته).
  • مع Flexible(tight) يُعطي قيودًا صارمة (يلزم الطفل بملء حصته).
متى أستخدم Flexible بدل Expanded؟
  • عندما لا تريد إجبار الطفل على ملء كل المساحة (مثلاً نص قد يكون قصيرًا).
  • عندما تريد توزيع المساحة لكن تسمح لبعض العناصر أن تبقى بحجم محتواها.
  • عند المزج بين عناصر يجب أن تتمدد وأخرى لا (موازنة دقيقة للواجهة).
أمثلة عملية
1) نص يتمدّد “بقدر حاجته” داخل Row
Row(
  children: [
    Icon(Icons.info),
    SizedBox(width: 8),
    Flexible( // loose: يسمح للنص بأخذ حاجته فقط
      child: Text(
        'شرح موجز قد يلتف على سطرين كحد أقصى',
        maxLines: 2,
        overflow: TextOverflow.ellipsis,
      ),
    ),
    SizedBox(width: 8),
    Icon(Icons.chevron_right),
  ],
)
Dart
2) فرض الامتلاء مثل Expanded (tight)
Row(
  children: [
    Container(width: 80, height: 40, color: Colors.red),
    Flexible(
      fit: FlexFit.tight, // = نفس سلوك Expanded
      flex: 2,
      child: Container(height: 40, color: Colors.green),
    ),
    Flexible(
      fit: FlexFit.tight,
      flex: 1,
      child: Container(height: 40, color: Colors.blue),
    ),
  ],
)
Dart
3) داخل Column مع عنصر قابل للتمرير
Column(
  children: [
    const ListTile(title: Text('العنوان')),
    // Flexible يعمل، لكن إن أردت ملء المساحة تمامًا استخدم Expanded
    Flexible(
      child: ListView.builder(
        itemCount: 30,
        itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
      ),
    ),
  ],
)
Dart
4) معالجة صور/محتوى قد يفيض
Row(
  children: [
    Flexible(
      child: AspectRatio(
        aspectRatio: 16 / 9,
        child: Image.network('https://picsum.photos/800', fit: BoxFit.cover),
      ),
    ),
    const SizedBox(width: 8),
    Flexible(
      child: Text(
        'تفاصيل الصورة…',
        maxLines: 3,
        overflow: TextOverflow.ellipsis,
      ),
    ),
  ],
)
Dart
ثانيًا: Spacer
ما هو؟

Spacer ويدجت خاصة تُستخدم داخل Row/Column/Flex لإضافة مساحة مرنة فارغة.
داخليًا يشبه:

Expanded(flex: n, child: SizedBox.shrink())
Dart

أي أنه لا يحتوي محتوى، فقط يملأ الفراغ بنسبة flex.

الصيغة
const Spacer({ Key? key, int flex = 1 })
Dart
استخداماته الشائعة
  • دفع عنصر إلى الطرف المقابل (push).
  • إنشاء فراغات متساوية/نسبية بين العناصر.
  • محاذاة عنوان في الوسط بين أيقونتين.
  • دفع زر/محتوى لأسفل الشاشة داخل Column.
أمثلة عملية
1) دفع العناصر للأطراف
Row(
  children: const [
    Icon(Icons.home),
    Spacer(),             // يملأ كل الفراغ بين الأيقونات
    Icon(Icons.search),
  ],
)
Dart
2) تباعد متساوٍ
Row(
  children: const [
    Icon(Icons.call),
    Spacer(),
    Icon(Icons.message),
    Spacer(),
    Icon(Icons.videocam),
  ],
)
Dart
3) عنوان في المنتصف بأسلوب بسيط
Row(
  children: const [
    Icon(Icons.arrow_back),
    Spacer(),
    Text('العنوان', style: TextStyle(fontWeight: FontWeight.bold)),
    Spacer(),
    Icon(Icons.more_vert),
  ],
)
Dart
4) دفع زر لأسفل الشاشة
Column(
  children: [
    const Text('محتوى طويل هنا...'),
    const Spacer(), // يدفع ما بعده لأسفل
    ElevatedButton(onPressed: () {}, child: const Text('متابعة')),
  ],
)
Dart
مقارنة سريعة: Expanded vs Flexible vs Spacer
الخاصية/السلوكExpandedFlexible (loose)Flexible (tight)Spacer
الملء الإجبارينعملانعمنعم (مسافة فارغة)
يأخذ حجم المحتوى فقطلاممكنلا
وجود محتوىنعمنعمنعملا (فراغ)
الاستخدام المثاليملء الحصّة بالكاملعنصر يأخذ ما يحتاجهمثل Expanded لكن بإدارة دقيقةإنشاء مسافات مرنة
الصيغةExpanded(flex, child)Flexible(flex, child)Flexible(flex, fit: tight, child)Spacer(flex)
أفضل الممارسات والنصائح
  • داخل Row/Column فقط: الثلاثة (Expanded, Flexible, Spacer) تعمل داخل Flex وأبنائه فقط. أي استخدام خارج ذلك يسبب خطأ (Incorrect use of ParentDataWidget).
  • خلطٌ ذكي: استخدم Expanded عندما تريد ملء كامل الحصة، وFlexible(loose) عندما تريد مرونة لحجم الطفل، وSpacer للمسافات.
  • النصوص والصور: عند وجود نص قد يطول، لفّه بـ Flexible مع overflow: TextOverflow.ellipsis.
    للصور الكبيرة، اضبط fit و/أو لفها بقيود (AspectRatio, ConstrainedBox) لتفادي overflow.
  • تجنّب تعارضات المحاذاة: وضع Expanded/Spacer مع MainAxisAlignment.spaceBetween قد يعطي نتائج غير متوقعة لأن العناصر المتمددة تستهلك الفراغ. استخدم واحدًا من الأسلوبين لا كلاهما.
  • استخدم flex كنِسَب: مثل 1:2:3 لتقسيم المساحة بدون حسابات بيكسل ثابتة.
خلاصة سريعة
  • Flexible يمنحك مرونة في ملء الحصّة:
    • loose (افتراضي): الطفل يأخذ ما يحتاجه فقط.
    • tight: مثل Expanded (يمتلئ بالكامل).
  • Spacer يصنع فراغًا مرنًا (بدون محتوى) لتوزيع/دفع العناصر بسهولة.
  • اختَر بينهم حسب احتياجك: ملء كامل (Expanded/tight) vs مرونة (Flexible/loose) vs مسافة (Spacer).