أولاً: 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),
],
)
Dart2) فرض الامتلاء مثل 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),
),
],
)
Dart3) داخل Column مع عنصر قابل للتمرير
Column(
children: [
const ListTile(title: Text('العنوان')),
// Flexible يعمل، لكن إن أردت ملء المساحة تمامًا استخدم Expanded
Flexible(
child: ListView.builder(
itemCount: 30,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
),
],
)
Dart4) معالجة صور/محتوى قد يفيض
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),
],
)
Dart2) تباعد متساوٍ
Row(
children: const [
Icon(Icons.call),
Spacer(),
Icon(Icons.message),
Spacer(),
Icon(Icons.videocam),
],
)
Dart3) عنوان في المنتصف بأسلوب بسيط
Row(
children: const [
Icon(Icons.arrow_back),
Spacer(),
Text('العنوان', style: TextStyle(fontWeight: FontWeight.bold)),
Spacer(),
Icon(Icons.more_vert),
],
)
Dart4) دفع زر لأسفل الشاشة
Column(
children: [
const Text('محتوى طويل هنا...'),
const Spacer(), // يدفع ما بعده لأسفل
ElevatedButton(onPressed: () {}, child: const Text('متابعة')),
],
)
Dartمقارنة سريعة: Expanded vs Flexible vs Spacer
الخاصية/السلوك | Expanded | Flexible (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).