ما هو 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 باختصار)
- يقيّم الـ Row/Column العناصر غير المرنة أولًا (مثل SizedBox بعرض ثابت، أو Widgets لا تستخدم Expanded/Flexible)، ويحجز لها مكانًا.
- يحسب المساحة المتبقية.
- يوزّع الباقي على العناصر المتمددة حسب نِسَب 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
| المقارنة | Expanded | Flexible | Spacer |
|---|---|---|---|
| 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'))),
],
)DartExpanded هنا يحل مشكلة «ارتفاع غير محدود» لقائمة داخل Column.
4) استخدام Spacer لمسافة مرنة
Row(
children: [
Icon(Icons.home),
Spacer(), // يملأ المسافة
Icon(Icons.search),
SizedBox(width: 8),
Icon(Icons.settings),
],
)Dart5) مقارنة 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).
أخطاء شائعة وحلولها
- Expanded داخل غير Flex → خطأ ParentDataWidget
الحل: ضعه داخلRow/Column/Flexفقط. - ListView داخل Column بدون Expanded → خطأ unbounded height
الحل: لفّ الـListViewبـExpandedأوSizedBoxبارتفاع محدد. - تعارض بين Expanded و MainAxisAlignment.spaceBetween
عندما تُضيفExpandedثم تستخدمspaceBetween، قد لا ترى المسافات المتوقعة لأن العناصر المتمددة تستهلك الفراغ.
الحل: إما تستخدمSpacerلتوليد الفراغات، أو غيّرMainAxisAlignmentلما يناسب التصميم. - طفل Expanded يحاول تجاوز قيوده (مثلاً صورة ضخمة بدون fit)
الحل: اضبط قيود الطفل (BoxFit للصورة، أو قيود عبرConstrainedBox/FittedBox).
متى أستخدم Expanded تحديدًا؟
- عندما تريد عنصرًا يمتد لملء المساحة المتبقية على محور Row/Column.
- لتقسيم المساحة بنِسَب باستخدام
flex. - لحل مشاكل القياس مع عناصر التمرير داخل Column.
خلاصة سريعة
Expanded=Flexible(fit: FlexFit.tight)داخل Row/Column/Flex فقط.- يوزّع المساحة المتبقية حسب
flex. - استخدم
Spacerللمسافات المرنة، وFlexibleعندما لا تريد إجبار الطفل على ملء حصته بالكامل