ماذا سنتعلم اليوم؟
سنتعلم كيفية ترتيب العناصر على الشاشة باستخدام ودجات Row و Column في Flutter، وكيفية استغلال المساحات المتاحة بفعالية لتحقيق تصاميم مرنة وجذابة.
الخطوة 1: أساسيات Row و Column
تُعد Row و Column من الودجات الأساسية في Flutter لتنظيم الودجات الأخرى. Row تقوم بترتيب العناصر أفقياً، بينما Column تقوم بترتيبها عمودياً. كلاهما يقبل قائمة من الودجات كأبناء (children).
ملاحظة تقنية:RowوColumnهما ودجتان "غير محدودتي الحجم" في الاتجاه الرئيسي إذا لم يتم تحديد حجمهما أو وضعها داخل ودجة تحدد الحجم (مثلExpandedأوSizedBox). هذا يعني أنهما ستحاولان شغل أكبر قدر ممكن من المساحة في اتجاههما الرئيسي إذا لم يتم تقييدهما.
لنبدأ بمثال بسيط يوضح كيفية استخدام Column لترتيب ثلاثة صناديق ملونة عمودياً:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('ترتيب العناصر: Row و Column')),
body: Column( // Column تقوم بترتيب الأطفال عمودياً
children: <Widget>[
Container( // صندوق أحمر
color: Colors.red,
height: 100,
width: 100,
child: const Center(child: Text('صندوق 1', style: TextStyle(color: Colors.white))),
),
Container( // صندوق أخضر
color: Colors.green,
height: 100,
width: 100,
child: const Center(child: Text('صندوق 2', style: TextStyle(color: Colors.white))),
),
Container( // صندوق أزرق
color: Colors.blue,
height: 100,
width: 100,
child: const Center(child: Text('صندوق 3', style: TextStyle(color: Colors.white))),
),
],
),
),
);
}
}
الخطوة 2: التحكم في المحاذاة (MainAxisAlignment, CrossAxisAlignment)
يمكننا التحكم في كيفية محاذاة الأطفال داخل Row أو Column باستخدام الخصائص mainAxisAlignment و crossAxisAlignment.
mainAxisAlignment: تتحكم في محاذاة الأطفال على طول المحور الرئيسي (أفقي لـRow، وعمودي لـColumn).crossAxisAlignment: تتحكم في محاذاة الأطفال على طول المحور المتقاطع (عمودي لـRow، وأفقي لـColumn).
لنعدل المثال السابق لنجعل الصناديق تتباعد بالتساوي على المحور الرئيسي (عمودياً) وتتمركز أفقياً:
import 'package:flutter/material.dart';
// ... (بقية الكود كما هو في الخطوة 1 حتى class MyApp)
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('ترتيب العناصر: Row و Column')),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // توزيع الأطفال بالتساوي على المحور الرئيسي (عمودي)
crossAxisAlignment: CrossAxisAlignment.center, // محاذاة الأطفال في المنتصف على المحور المتقاطع (أفقي)
children: <Widget>[
Container(
color: Colors.red,
height: 80, // قمنا بتقليل الارتفاع قليلاً
width: 80,
child: const Center(child: Text('صندوق 1', style: TextStyle(color: Colors.white))),
),
Container(
color: Colors.green,
height: 80,
width: 80,
child: const Center(child: Text('صندوق 2', style: TextStyle(color: Colors.white))),
),
Container(
color: Colors.blue,
height: 80,
width: 80,
child: const Center(child: Text('صندوق 3', style: TextStyle(color: Colors.white))),
),
],
),
),
);
}
}
الخطوة 3: استغلال المساحات (Expanded, Flexible)
في كثير من الأحيان، نحتاج أن تشغل بعض العناصر المساحة المتبقية على الشاشة، أو أن تتكيف مع المساحة المتاحة. هنا يأتي دور الودجات Expanded و Flexible. كلاهما يجب أن يكونا أبناء لـ Row أو Column.
Expanded: تجعل الودجة التي تحتويها تشغل كل المساحة المتبقية على المحور الرئيسي (بعد أن تأخذ الودجات الأخرى حجمها الطبيعي). لها خاصيةflexتحدد نسبة المساحة التي ستشغلها مقارنة بـExpandedالأخرى.Flexible: تشبهExpandedولكنها أكثر مرونة. لا تجبر الودجة على شغل المساحة المتبقية بالكامل، بل تسمح لها بالنمو أو الانكماش ضمن حدود. لها خاصيةfit(FlexFit.tightمثلExpanded، وFlexFit.looseتسمح للودجة بأخذ حجمها الأصلي إذا لم تكن هناك مساحة إضافية).
لنقم بإنشاء Row تحتوي على زرين، ونجعل الزر الثاني يشغل كل المساحة المتبقية:
import 'package:flutter/material.dart';
// ... (بقية الكود كما هو في الخطوة 1 حتى class MyApp)
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('استغلال المساحات')),
body: Row( // Row لترتيب العناصر أفقياً
children: <Widget>[
Container( // زر ثابت الحجم
color: Colors.orange,
width: 100,
height: 50,
child: const Center(child: Text('زر ثابت', style: TextStyle(color: Colors.white))),
),
Expanded( // هذا الزر سيشغل كل المساحة المتبقية أفقياً
flex: 2, // يمكن تعديل نسبة المساحة التي يشغلها
child: Container(
color: Colors.purple,
height: 50,
child: const Center(child: Text('زر ممتد', style: TextStyle(color: Colors.white))),
),
),
Flexible( // هذا الزر مرن، سيأخذ حجمه الأصلي إذا لم يكن هناك ضغط
flex: 1, // نسبة مرونته
child: Container(
color: Colors.teal,
height: 50,
width: 80, // حجم أصلي محدد
child: const Center(child: Text('زر مرن', style: TextStyle(color: Colors.white))),
),
),
],
),
),
);
}
}
الكود النهائي الكامل
هذا هو الكود الكامل الذي يجمع كل المفاهيم التي تعلمناها، ويمكنك تجربته مباشرة في بيئة Flutter:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false, // لإزالة شارة Debug
home: Scaffold(
appBar: AppBar(
title: const Text('ترتيب العناصر واستغلال المساحات'),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
body: Column( // العمود الرئيسي الذي يحتوي على الأمثلة
crossAxisAlignment: CrossAxisAlignment.stretch, // لجعل الأطفال يملؤون العرض
children: <Widget>[
// مثال Row مع محاذاة واستغلال مساحة
Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200],
child: const Text('مثال Row: محاذاة واستغلال مساحة', style: TextStyle(fontWeight: FontWeight.bold)),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // توزيع العناصر مع مسافة بينها
children: <Widget>[
Container(
color: Colors.red,
width: 60,
height: 60,
child: const Center(child: Text('A', style: TextStyle(color: Colors.white))),
),
Expanded( // هذا الصندوق سيأخذ كل المساحة المتبقية
child: Container(
color: Colors.green,
height: 60,
margin: const EdgeInsets.symmetric(horizontal: 8.0), // هامش جانبي
child: const Center(child: Text('B (ممتد)', style: TextStyle(color: Colors.white))),
),
),
Container(
color: Colors.blue,
width: 60,
height: 60,
child: const Center(child: Text('C', style: TextStyle(color: Colors.white))),
),
],
),
const SizedBox(height: 20), // مسافة فاصلة
// مثال Column مع محاذاة واستغلال مساحة
Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200],
child: const Text('مثال Column: محاذاة واستغلال مساحة', style: TextStyle(fontWeight: FontWeight.bold)),
),
Expanded( // لجعل الـ Column تملأ المساحة المتبقية عمودياً
child: Column(
mainAxisAlignment: MainAxisAlignment.center, // توسيط العناصر عمودياً
crossAxisAlignment: CrossAxisAlignment.center, // توسيط العناصر أفقياً
children: <Widget>[
Container(
color: Colors.orange,
width: 120,
height: 50,
child: const Center(child: Text('عنصر 1', style: TextStyle(color: Colors.white))),
),
const SizedBox(height: 10), // مسافة بين العناصر
Flexible( // عنصر مرن، يأخذ حجمه الأصلي إذا كان هناك مساحة كافية
child: Container(
color: Colors.purple,
width: 120,
height: 80,
child: const Center(child: Text('عنصر 2 (مرن)', style: TextStyle(color: Colors.white))),
),
),
const SizedBox(height: 10),
Container(
color: Colors.teal,
width: 120,
height: 50,
child: const Center(child: Text('عنصر 3', style: TextStyle(color: Colors.white))),
),
],
),
),
],
),
),
);
}
}
النتيجة المتوقعة
عند تشغيل الكود النهائي، ستظهر شاشة التطبيق مقسمة إلى قسمين رئيسيين. القسم العلوي سيعرض ثلاثة مربعات (أحمر، أخضر، أزرق) مرتبة أفقياً داخل Row. المربع الأخضر سيشغل معظم المساحة الأفقية المتبقية (بفضل Expanded)، بينما المربعان الأحمر والأزرق سيحتفظان بحجمهما الثابت، مع وجود مسافات متساوية بينها. القسم السفلي سيعرض ثلاثة مربعات أخرى (برتقالي، بنفسجي، تركوازي) مرتبة عمودياً داخل Column. ستكون هذه المربعات متمركزة عمودياً وأفقياً داخل مساحتها المخصصة، حيث سيكون المربع البنفسجي مرناً في حجمه.
سيوضح هذا الدرس بوضوح كيفية استخدام Row و Column مع mainAxisAlignment و crossAxisAlignment للتحكم في المحاذاة، وكيفية استخدام Expanded و Flexible لتحقيق تصاميم مرنة تستغل المساحات المتاحة بذكاء.