تخطَّ إلى المحتوى

Row و Column في Flutter: ترتيب العناصر

الدرس 5 من 33 · Flutter

انظر إلى أي تطبيق على هاتفك: قائمة محادثات فيها صورة ثم اسم ورسالة ثم وقت، وشريط علوي فيه عنوان ثم أيقونات. كل هذه التصميمات مبنية من قطعتين أساسيتين: Row لترتيب العناصر أفقياً، و Column لترتيبها رأسياً. في هذا الدرس ستتقن الاثنين، وتتعلم كيف توزع المساحة بينهما.

const Column(
children: [
Text('الأول'),
Text('الثاني'),
Text('الثالث'),
],
)

Column ترتب أبناءها فوق بعضهم من الأعلى للأسفل. و Row بنفس الصيغة تماماً، لكنها ترتبهم بجانب بعضهم. وفي التطبيق العربي يبدأ Row من اليمين تلقائياً.

تخيّل Column مثل رفوف المكتبة فوق بعضها، و Row مثل الكتب المصفوفة على الرف الواحد.

هذه أهم فكرة في الدرس:

  • المحور الرئيسي (Main Axis): اتجاه الترتيب نفسه. في Row أفقي، وفي Column رأسي.
  • المحور المتقاطع (Cross Axis): الاتجاه العمودي عليه. في Row رأسي، وفي Column أفقي.

ولكل محور خاصية تحاذي العناصر عليه:

const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('مدرستي', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
Row(
children: [
Icon(Icons.search),
SizedBox(width: 12),
Icon(Icons.notifications),
],
),
],
)

هذا شريط علوي: العنوان في جهة، والأيقونتان في الجهة الأخرى. و spaceBetween وزعت المساحة الفارغة بينهما.

قيم mainAxisAlignment: start (البداية)، و center، و end، و spaceBetween (الأول والأخير على الأطراف)، و spaceAround، و spaceEvenly (مسافات متساوية تماماً).

قيم crossAxisAlignment: start و center و end، و stretch (تمديد العناصر لتملأ المحور المتقاطع).

ماذا لو أردت ثلاثة صناديق تملأ عرض الشاشة كله؟ لف كل واحد بـ Expanded:

Row(
children: [
Expanded(child: _box('HTML', Colors.orange)),
const SizedBox(width: 8),
Expanded(child: _box('CSS', Colors.blue)),
const SizedBox(width: 8),
Expanded(flex: 2, child: _box('JavaScript', Colors.amber)),
],
)
  • Expanded تعني: «خذ نصيبك من المساحة المتبقية».
  • flex: 2 تعني «خذ نصيبين». هنا JavaScript عرضه ضعف كل من HTML و CSS.
  • SizedBox(width: 8) تضع فراغاً ثابتاً بين الصناديق.

تخيّل Expanded مثل تقسيم قطعة أرض بين ورثة: كل واحد له أسهم (flex)، وتُقسم الأرض حسب الأسهم.

والدالة _box دالة مساعدة صغيرة تُرجع صندوقاً ملوناً، كتبناها لنتجنب تكرار نفس الكود ثلاث مرات:

Widget _box(String label, Color color) {
return Container(
height: 60,
alignment: Alignment.center,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(10)),
child: Text(label, style: const TextStyle(color: Colors.white)),
);
}

مثال واقعي: عنصر في قائمة محادثات

Section titled “مثال واقعي: عنصر في قائمة محادثات”
const Row(
children: [
CircleAvatar(child: Icon(Icons.person)),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('سارة أحمد', style: TextStyle(fontWeight: FontWeight.bold)),
Text('أنهت درس الحلقات اليوم'),
],
),
),
Text('10:30'),
],
)

صورة دائرية، ثم عمود فيه الاسم والرسالة يأخذ كل المساحة المتبقية بفضل Expanded، ثم الوقت في الطرف الآخر. و CrossAxisAlignment.start جعلت النصين يبدآن من نفس الحافة بدلاً من التوسيط.

هذا هو الأسلوب الذي ستبني به أغلب الشاشات: Row داخل Column داخل Row، مثل علب داخل علب.

أحياناً تحتاج وضع عنصر فوق آخر، مثل شارة «جديد» فوق صورة:

SizedBox(
height: 120,
child: Stack(
children: [
Container(color: Colors.indigo.shade100),
const Positioned(
top: 8,
left: 8,
child: Chip(label: Text('جديد')),
),
const Center(child: Text('صورة الكورس')),
],
),
)

Stack ترص أبناءها فوق بعضهم مثل أوراق اللعب: الأول في الأسفل، والأخير في الأعلى. و Positioned تحدد مكان العنصر بالمسافة من الحواف.

خطأ المساحة الشهير: Overflow

Section titled “خطأ المساحة الشهير: Overflow”

إذا وضعت في Row عناصر أعرض من الشاشة، سترى شريطاً أصفر وأسود مخططاً على الحافة، ورسالة مثل A RenderFlex overflowed by 42 pixels. معناها: «المحتوى أكبر من المساحة المتاحة».

الحلول:

  • لف العنصر الطويل (مثل نص طويل) بـ Expanded ليأخذ المساحة المتاحة فقط وينزل لسطر جديد.
  • استخدم Wrap بدل Row، فتنتقل العناصر لسطر جديد تلقائياً.
  • إذا كان المحتوى أطول من الشاشة رأسياً، لف العمود بـ SingleChildScrollView ليصبح قابلاً للتمرير.
  1. ابنِ الشريط العلوي (عنوان وأيقونتان) بـ Row و spaceBetween.
  2. ابنِ الصناديق الثلاثة بـ Expanded، وجرّب قيماً مختلفة لـ flex.
  3. ابنِ عنصر المحادثة، ثم اكتب رسالة طويلة جداً وتأكد أنها تنزل لسطر جديد بدلاً من الـ Overflow.
  4. تحدٍّ: صمم بطاقة منتج: صورة (Container ملون) فوقها شارة خصم بـ Stack، وتحتها صف فيه الاسم والسعر.
  • Overflow في Row: نص طويل بدون Expanded. لفه بـ Expanded أو Flexible.
  • Expanded خارج Row أو Column: Expanded تعمل فقط كابن مباشر لـ Row أو Column.
  • الخلط بين المحورين: في Column المحاذاة الأفقية هي crossAxisAlignment وليست mainAxisAlignment.

سؤال

في Column، أي خاصية تتحكم في المحاذاة الأفقية للعناصر؟

سؤال

ثلاثة عناصر Expanded في Row، قيم flex لها 1 و 1 و 2. ما نصيب الثالث من المساحة؟

  • Column ترتب رأسياً، و Row أفقياً، وفي التطبيق العربي يبدأ الصف من اليمين.
  • mainAxisAlignment للمحور الرئيسي، و crossAxisAlignment للمحور المتقاطع.
  • Expanded توزع المساحة المتبقية حسب flex، و SizedBox لفراغ ثابت.
  • Stack و Positioned لوضع العناصر فوق بعضها.
  • خطأ Overflow يعني محتوى أكبر من المساحة، وحله Expanded أو Wrap أو التمرير.