Expanded و Flexible
مقدمة إلى العناصر المرنة
عند بناء التخطيطات في Flutter، غالباً ما تحتاج أن تتشارك العناصر الفرعية المساحة المتاحة بشكل تناسبي بدلاً من استخدام أحجام ثابتة. عنصرا Expanded و Flexible يحلان هذه المشكلة بإعطاء العناصر الفرعية القدرة على النمو والتقلص داخل Row أو Column أو Flex الأب.
كلا العنصرين يغلّفان عنصراً فرعياً ويعيّنان له عامل مرونة يحدد مقدار المساحة المتبقية التي يحصل عليها. الفرق الرئيسي يكمن في كيفية تعاملهما مع المساحة المخصصة.
عنصر Expanded
يجبر Expanded عنصره الفرعي على ملء كل المساحة المتبقية المتاحة على المحور الرئيسي. يجب على العنصر الفرعي أن يأخذ بالضبط المساحة المخصصة له -- لا أكثر ولا أقل.
مثال أساسي لـ Expanded
Row(
children: <Widget>[
Container(width: 80, height: 50, color: Colors.red),
Expanded(
child: Container(height: 50, color: Colors.green),
),
Container(width: 80, height: 50, color: Colors.blue),
],
)
// أحمر: 80 بكسل | أخضر: يملأ المتبقي | أزرق: 80 بكسل
في هذا المثال، الحاويتان الحمراء والزرقاء لهما عرض ثابت 80 بكسل لكل منهما. الحاوية الخضراء، المغلّفة بـ Expanded، تأخذ كل العرض المتبقي. إذا كان Row بعرض 400 بكسل، تحصل الحاوية الخضراء على 400 - 80 - 80 = 240 بكسل.
عنصر Flexible
Flexible مشابه لـ Expanded لكنه يعطي العنصر الفرعي خيار أن يكون أصغر من المساحة المخصصة. يمكن للعنصر الفرعي تحديد حجمه حتى الحد الأقصى للمساحة المخصصة لكنه غير مجبر على ملئها بالكامل.
مثال أساسي لـ Flexible
Row(
children: <Widget>[
Flexible(
child: Container(
width: 100, // يستخدم فقط 100 بكسل حتى لو كان المتاح أكثر
height: 50,
color: Colors.orange,
),
),
Container(width: 80, height: 50, color: Colors.purple),
],
)
// برتقالي: حتى المساحة المخصصة لكن 100 بكسل فقط | بنفسجي: 80 بكسل
مع Flexible، تطلب الحاوية البرتقالية 100 بكسل. إذا كانت المساحة المخصصة 320 بكسل، تأخذ الحاوية 100 بكسل فقط -- المساحة المتبقية 220 بكسل تبقى فارغة. مع Expanded، كانت ستُجبر على التمدد إلى 320 بكسل.
عامل flex
يقبل كلا من Expanded و Flexible معامل flex (الافتراضي: 1) الذي يحدد نسبة المساحة المتبقية التي يحصل عليها كل عنصر فرعي. تُقسم المساحة وفقاً لنسبة قيم flex.
مساحة تناسبية باستخدام flex
Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(height: 50, color: Colors.red),
),
Expanded(
flex: 1,
child: Container(height: 50, color: Colors.green),
),
Expanded(
flex: 1,
child: Container(height: 50, color: Colors.blue),
),
],
)
// إجمالي flex = 2 + 1 + 1 = 4
// أحمر: 2/4 = 50% | أخضر: 1/4 = 25% | أزرق: 1/4 = 25%
flex: 3 و flex: 7 يعطيان تقسيم 30%/70%.FlexFit: Tight مقابل Loose
الفرق الأساسي بين Expanded و Flexible يعود إلى خاصية FlexFit:
- FlexFit.tight (يستخدمه Expanded) -- يجب على العنصر الفرعي ملء المساحة المخصصة بالكامل. يُجبر على أن يكون بالضبط بحجم تخصيصه.
- FlexFit.loose (يستخدمه Flexible) -- يمكن للعنصر الفرعي أن يكون أصغر من المساحة المخصصة. يحدد حجمه بشكل طبيعي، حتى الحد الأقصى للتخصيص.
FlexFit.tight مقابل FlexFit.loose
// Expanded هو اختصار لـ:
Flexible(
fit: FlexFit.tight,
child: Container(height: 50, color: Colors.red),
)
// Flexible يستخدم افتراضياً:
Flexible(
fit: FlexFit.loose,
child: Container(height: 50, color: Colors.blue),
)
// يمكنك جعل Flexible يتصرف مثل Expanded:
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(height: 50, color: Colors.green),
)
Expanded هو حرفياً غلاف مريح حول Flexible(fit: FlexFit.tight). هما متطابقان وظيفياً عند استخدام tight fit.عنصر Spacer
عنصر Spacer هو عنصر مريح ينشئ مساحة فارغة ممتدة. هو مكافئ لـ Expanded(child: SizedBox.shrink()). يفيد Spacer في دفع العناصر الفرعية بعيداً عن بعضها بدون إنشاء عناصر مرئية.
استخدام Spacer
Row(
children: <Widget>[
Text('يسار', style: TextStyle(fontSize: 18)),
Spacer(), // يدفع 'يسار' و 'يمين' إلى الطرفين المتقابلين
Text('يمين', style: TextStyle(fontSize: 18)),
],
)
// مع عامل flex:
Row(
children: <Widget>[
Text('بداية'),
Spacer(flex: 2), // ضعف المساحة
Text('وسط'),
Spacer(flex: 1), // نصف المساحة
Text('نهاية'),
],
)
Expanded(child: SizedBox()) ويوصّل القصد بشكل أفضل. استخدمه عندما تحتاج مساحة فارغة مرنة بين العناصر.توزيع المساحة بشكل تناسبي
متطلب شائع هو تقسيم التخطيط إلى أقسام تناسبية. إليك كيفية إنشاء نسب تقسيم شائعة:
نسب تقسيم شائعة
// تقسيم 50/50
Row(
children: [
Expanded(flex: 1, child: Container(color: Colors.red)),
Expanded(flex: 1, child: Container(color: Colors.blue)),
],
)
// تقسيم 1/3 و 2/3
Row(
children: [
Expanded(flex: 1, child: Container(color: Colors.red)),
Expanded(flex: 2, child: Container(color: Colors.blue)),
],
)
// تقسيم 25/50/25
Row(
children: [
Expanded(flex: 1, child: Container(color: Colors.red)),
Expanded(flex: 2, child: Container(color: Colors.green)),
Expanded(flex: 1, child: Container(color: Colors.blue)),
],
)
دمج Expanded مع عناصر ثابتة الحجم
النمط الأكثر عملية هو مزج العناصر ثابتة الحجم مع عناصر Expanded. يخصص Flutter المساحة أولاً للعناصر الفرعية ثابتة الحجم، ثم يوزع المساحة المتبقية بين عناصر Expanded/Flexible بناءً على عوامل flex الخاصة بها.
تخطيط ثابت + ممتد
Row(
children: <Widget>[
// ثابت: صورة رمزية (56 بكسل)
CircleAvatar(radius: 28, child: Icon(Icons.person)),
SizedBox(width: 12),
// مرن: يأخذ المساحة المتبقية
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('أحمد علي', style: TextStyle(fontWeight: FontWeight.bold)),
Text('متصل', style: TextStyle(color: Colors.green, fontSize: 12)),
],
),
),
// ثابت: زر إجراء
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
],
)
مثال عملي: تخطيطات مقسمة
تخطيط بلوحتين حيث اللوحة اليسرى أضيق واللوحة اليمنى أعرض:
تخطيط مقسم بلوحتين
Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Colors.grey[200],
child: ListView(
children: [
ListTile(title: Text('العنصر 1')),
ListTile(title: Text('العنصر 2')),
ListTile(title: Text('العنصر 3')),
],
),
),
),
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(16),
child: Text('منطقة المحتوى', style: TextStyle(fontSize: 24)),
),
),
],
)
مثال عملي: شريط جانبي + محتوى
تخطيط تطبيق شائع مع شريط جانبي ثابت العرض ومنطقة محتوى مرنة:
تخطيط شريط جانبي + محتوى
Row(
children: <Widget>[
// شريط جانبي ثابت
Container(
width: 250,
color: Colors.blueGrey[900],
child: Column(
children: [
SizedBox(height: 40),
Text('القائمة', style: TextStyle(color: Colors.white, fontSize: 20)),
ListTile(
leading: Icon(Icons.dashboard, color: Colors.white),
title: Text('لوحة التحكم', style: TextStyle(color: Colors.white)),
),
ListTile(
leading: Icon(Icons.settings, color: Colors.white),
title: Text('الإعدادات', style: TextStyle(color: Colors.white)),
),
],
),
),
// محتوى مرن
Expanded(
child: Container(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('لوحة التحكم', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold)),
SizedBox(height: 16),
Text('مرحباً بعودتك! إليك نظرتك العامة.'),
],
),
),
),
],
)
مثال عملي: أعمدة تناسبية
صف إحصائيات حيث تأخذ كل بطاقة إحصائية مساحة متساوية:
بطاقات إحصائية متساوية العرض
Row(
children: <Widget>[
Expanded(
child: Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Icon(Icons.people, size: 32, color: Colors.blue),
SizedBox(height: 8),
Text('1,234', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text('مستخدمون', style: TextStyle(color: Colors.grey)),
],
),
),
),
),
Expanded(
child: Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Icon(Icons.shopping_cart, size: 32, color: Colors.green),
SizedBox(height: 8),
Text('567', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text('طلبات', style: TextStyle(color: Colors.grey)),
],
),
),
),
),
Expanded(
child: Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Icon(Icons.attach_money, size: 32, color: Colors.orange),
SizedBox(height: 8),
Text('\$9,876', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
Text('الإيرادات', style: TextStyle(color: Colors.grey)),
],
),
),
),
),
],
)
SizedBox في السياقات القابلة للتمرير بدلاً من ذلك.الملخص
- Expanded يجبر عنصره الفرعي على ملء كل المساحة المخصصة (FlexFit.tight).
- Flexible يسمح لعنصره الفرعي أن يكون أصغر من المساحة المخصصة (FlexFit.loose).
- عامل
flexيتحكم في توزيع المساحة التناسبي -- flex أعلى يعني مساحة أكبر. - Spacer ينشئ مساحة فارغة مرنة، مفيد لدفع العناصر بعيداً عن بعضها.
- يخصص Flutter المساحة للعناصر الفرعية ثابتة الحجم أولاً، ثم يوزع المساحة المتبقية بين عناصر flex.
- يجب أن يكون Expanded و Flexible عناصر فرعية مباشرة لـ Row أو Column أو Flex.
- لا تستخدم Expanded/Flexible داخل عناصر قابلة للتمرير بمحور رئيسي غير محدود.