Expanded

ما هو Expanded؟

Expanded هو ويدجت يُستخدم داخل عناصر مرنة (Flex) مثل Row وColumn وFlex ليجعل العنصر الابن يتمدّد ويأخذ المساحة المتبقية على محور الترتيب (المحور الرئيسي: أفقي في Row، عمودي في Column).
فعليًا، Expanded هو اختصار لـ:

Flexible(fit: FlexFit.tight, child: ...)
Dart

أي أن fit ثابت دائمًا على tight (إجبار الابن على ملء المساحة المخصصة له).

الصيغة
Expanded({
  int flex = 1,
  required Widget child,
})
Dart
  • flex: وزن العنصر في تقسيم المساحة المتبقية (افتراضيًا 1).
  • child: المحتوى.
كيف يعمل التقسيم (خوارزمية الـ Flex باختصار)
  1. يقيّم الـ Row/Column العناصر غير المرنة أولًا (مثل SizedBox بعرض ثابت، أو Widgets لا تستخدم Expanded/Flexible)، ويحجز لها مكانًا.
  2. يحسب المساحة المتبقية.
  3. يوزّع الباقي على العناصر المتمددة حسب نِسَب flex.
مثال رقمي سريع

صف (Row) عرضه الإجمالي 300px وفيه:

  • SizedBox(width: 50) (غير مرن)
  • Expanded(flex: 1, child: A)
  • Expanded(flex: 2, child: B)

المتبقي = 300 – 50 = 250px
المجموع الكلي للـ flex = 1 + 2 = 3

  • عرض A = 250 × (1/3) ≈ 83.33px
  • عرض B = 250 × (2/3) ≈ 166.67px
الفرق بين Expanded و Flexible و Spacer
المقارنةExpandedFlexibleSpacer
fitدائمًا tight (يمتد لملء حصته بالكامل)loose افتراضيًا (يمكن للابن ألا يملأ كل الحصة) أو tight إذا عيّنتاختصار جاهز لمسافة فارغة: Expanded(flex: n, child: SizedBox.shrink())
الاستخدامعند رغبتك أن يملأ الطفل كل حصتهعندما تريد أن يسمح للطفل بأخذ ما يحتاجه فقط داخل حصة مرنةلإضافة مسافة مرنة بين العناصر
الشيوعالأكثر استخدامًاللحالات الدقيقة/المخصّصةمريح للمسافات

ملاحظة: Expanded لا تعمل إلا داخل Row/Column/Flex. استخدامها في أبٍ آخر سيؤدي لخطأ: Incorrect use of ParentDataWidget.

أمثلة عملية
1) توزيع متساوٍ لعدة أزرار في صف
Row(
  children: [
    Expanded(child: ElevatedButton(onPressed: () {}, child: Text('يسار'))),
    SizedBox(width: 8),
    Expanded(child: ElevatedButton(onPressed: () {}, child: Text('وسط'))),
    SizedBox(width: 8),
    Expanded(child: ElevatedButton(onPressed: () {}, child: Text('يمين'))),
  ],
)
Dart

كل زر يأخذ ثلث العرض (مع فواصل ثابتة).

2) نسبة 1:3 بين شريط جانبي ومحتوى
Row(
  children: [
    Expanded(flex: 1, child: ColoredBox(color: Colors.grey.shade200)),
    Expanded(flex: 3, child: ColoredBox(color: Colors.white)),
  ],
)
Dart

اليمين/اليسار حسب اتجاه اللغة.

3) رأس ثابت + محتوى يتمدد + تذييل ثابت داخل Column
Column(
  children: [
    Container(height: 60, color: Colors.blue, child: Center(child: Text('Header'))),
    Expanded(
      child: ListView.builder(
        itemCount: 50,
        itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
      ),
    ),
    Container(height: 60, color: Colors.blue, child: Center(child: Text('Footer'))),
  ],
)
Dart

Expanded هنا يحل مشكلة «ارتفاع غير محدود» لقائمة داخل Column.

4) استخدام Spacer لمسافة مرنة
Row(
  children: [
    Icon(Icons.home),
    Spacer(),             // يملأ المسافة
    Icon(Icons.search),
    SizedBox(width: 8),
    Icon(Icons.settings),
  ],
)
Dart
5) مقارنة Expanded مقابل Flexible (loose)
Row(
  children: [
    Container(width: 80, height: 40, color: Colors.red),
    Flexible( // يسمح للطفل بما يحتاجه فقط داخل حصة مرنة
      child: ConstrainedBox(
        constraints: BoxConstraints(maxWidth: 120),
        child: Text('نص قصير', maxLines: 1, overflow: TextOverflow.ellipsis),
      ),
    ),
    Expanded( // سيملأ بقية العرض
      child: Container(height: 40, color: Colors.green),
    ),
  ],
)
Dart
أفضل الممارسات
  • استخدم Expanded لحل أخطاء الـ overflow/القياس داخل Row/Column عندما تريد ملء المساحة المتبقية.
  • استخدم flex لعمل نسب (1:2:3 …) بدل حسابات يدوية.
  • داخل Column مع عناصر قابلة للتمرير (مثل ListView أو SingleChildScrollView) ضع القائمة داخل Expanded لتجنب خطأ Vertical viewport was given unbounded height.
  • لا تُكثر من أعشاش Flex داخل Flex دون حاجة — بسّط الشجرة قدر الإمكان.
  • عند الحاجة لمسافات مرنة بين عناصر، استخدم Spacer بدل SizedBox كبير ثابت.
  • لا تضع Expanded داخل Widgets ليست Flex (مثل Stack، ListView، Wrap) — سيظهر خطأ. إن احتجت ملء المساحة في هذه الحالات، استخدم بدائل مناسبة (مثلاً داخل Stack استخدم Positioned.fill أو Align، وداخل ListView لا تحتاج Expanded أصلًا لأن ListView هو نفسه scrollable).
أخطاء شائعة وحلولها
  1. Expanded داخل غير Flex → خطأ ParentDataWidget
    الحل: ضعه داخل Row/Column/Flex فقط.
  2. ListView داخل Column بدون Expanded → خطأ unbounded height
    الحل: لفّ الـ ListView بـ Expanded أو SizedBox بارتفاع محدد.
  3. تعارض بين Expanded و MainAxisAlignment.spaceBetween
    عندما تُضيف Expanded ثم تستخدم spaceBetween، قد لا ترى المسافات المتوقعة لأن العناصر المتمددة تستهلك الفراغ.
    الحل: إما تستخدم Spacer لتوليد الفراغات، أو غيّر MainAxisAlignment لما يناسب التصميم.
  4. طفل Expanded يحاول تجاوز قيوده (مثلاً صورة ضخمة بدون fit)
    الحل: اضبط قيود الطفل (BoxFit للصورة، أو قيود عبر ConstrainedBox/FittedBox).
متى أستخدم Expanded تحديدًا؟
  • عندما تريد عنصرًا يمتد لملء المساحة المتبقية على محور Row/Column.
  • لتقسيم المساحة بنِسَب باستخدام flex.
  • لحل مشاكل القياس مع عناصر التمرير داخل Column.
خلاصة سريعة
  • Expanded = Flexible(fit: FlexFit.tight) داخل Row/Column/Flex فقط.
  • يوزّع المساحة المتبقية حسب flex.
  • استخدم Spacer للمسافات المرنة، وFlexible عندما لا تريد إجبار الطفل على ملء حصته بالكامل