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

هيكل الشاشة في Flutter: Scaffold و AppBar بالتفصيل

الدرس 12 من 33 · Flutter

استخدمت Scaffold في كل شاشة كتبتها حتى الآن، لكنك استعملت منه جزأين فقط: appBar و body. الحقيقة أن Scaffold فيه أماكن جاهزة لكل ما تراه في التطبيقات: شريط علوي بأزرار، وزر عائم، وشريط سفلي، وقائمة جانبية. في هذا الدرس ستتعرف على هذه الأماكن واحداً واحداً.

تخيّل Scaffold مثل مخطط شقة جاهز: الغرف محددة سلفاً، وأنت فقط تضع الأثاث في كل غرفة.

┌──────────────────────────┐
│ appBar │ ← الشريط العلوي
├──────────────────────────┤
│ │
│ body │ ← المحتوى الرئيسي
│ │
│ ( + ) │ ← floatingActionButton
├──────────────────────────┤
│ bottomNavigationBar │ ← الشريط السفلي
└──────────────────────────┘
الخاصية مكانها
appBar الشريط العلوي
body المساحة الرئيسية
floatingActionButton زر عائم فوق المحتوى
bottomNavigationBar شريط التنقل السفلي
drawer قائمة جانبية تنسحب من الحافة
backgroundColor لون خلفية الشاشة

لا يشترط أن تملأ كل الأماكن. استخدم ما تحتاجه فقط، وكلها اختيارية.

AppBar(
title: const Text('الرسائل'),
centerTitle: true,
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: () {},
),
actions: [
IconButton(
icon: const Icon(Icons.search),
tooltip: 'بحث',
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.notifications_none),
tooltip: 'الإشعارات',
onPressed: () {},
),
],
)
  • title: العنوان، وغالباً يكون Text، لكنه يقبل أي Widget، حتى حقل بحث.
  • centerTitle: هل يتوسط العنوان الشريط؟
  • backgroundColor: لون الشريط، و foregroundColor: لون العنوان والأيقونات معاً.
  • leading: Widget في بداية الشريط (اليمين في التطبيق العربي). إذا تركته فارغاً يضع Flutter سهم الرجوع تلقائياً في الشاشات الفرعية.
  • actions: قائمة أزرار في نهاية الشريط.
  • tooltip: نص يظهر عند الضغط المطول على الزر، ويقرؤه قارئ الشاشة للمكفوفين. اكتبه دائماً لأزرار الأيقونات.

القائمة المنسدلة: PopupMenuButton

Section titled “القائمة المنسدلة: PopupMenuButton”

زر الثلاث نقاط الذي يفتح قائمة صغيرة:

PopupMenuButton<String>(
onSelected: (value) {
debugPrint('اختار المستخدم: $value');
},
itemBuilder: (context) => const [
PopupMenuItem(value: 'settings', child: Text('الإعدادات')),
PopupMenuItem(value: 'help', child: Text('المساعدة')),
PopupMenuItem(value: 'logout', child: Text('تسجيل الخروج')),
],
)
  • <String>: نوع القيمة التي يحملها كل عنصر.
  • itemBuilder: دالة تُرجع عناصر القائمة، ولكل عنصر value تميّزه و child يظهر للمستخدم.
  • onSelected: تُستدعى عند اختيار عنصر، وتستقبل قيمته.

ضع هذا الزر كآخر عنصر في actions.

الزر العائم: FloatingActionButton

Section titled “الزر العائم: FloatingActionButton”

الزر العائم يمثل الإجراء الأهم في الشاشة: «رسالة جديدة» في تطبيق المحادثات، أو «إضافة مهمة» في تطبيق المهام.

FloatingActionButton.extended(
onPressed: () {},
icon: const Icon(Icons.edit),
label: const Text('رسالة جديدة'),
)
  • FloatingActionButton: دائري بأيقونة فقط.
  • FloatingActionButton.extended: ممتد بأيقونة ونص.
  • FloatingActionButton.small: نسخة أصغر.

ومكانه يتحدد من Scaffold بالخاصية floatingActionButtonLocation:

floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,

القيمة الافتراضية endFloat تضعه في نهاية الشاشة من الأسفل، و centerFloat في المنتصف.

SafeArea: احمِ محتواك من الحواف

Section titled “SafeArea: احمِ محتواك من الحواف”

الهواتف الحديثة فيها نتوء للكاميرا، وزوايا دائرية، وشريط للحالة فيه الساعة والبطارية. إذا لم تستخدم AppBar، فقد يختفي جزء من محتواك تحت هذه الأشياء. الحل SafeArea:

const Scaffold(
body: SafeArea(
child: Text('هذا النص لن يختفي تحت شريط الحالة'),
),
)

SafeArea تضيف مسافات حول ابنها بمقدار المناطق غير الآمنة في الجهاز الحالي. و AppBar يتعامل مع هذا تلقائياً، لذلك تحتاج SafeArea أساساً في الشاشات التي بلا شريط علوي، مثل شاشة الترحيب أو تسجيل الدخول.

الشريط السفلي والقائمة الجانبية

Section titled “الشريط السفلي والقائمة الجانبية”

bottomNavigationBar و drawer يستحقان درساً كاملاً، وستتعلمهما بالتفصيل في درس التنقل السفلي والتبويبات. لكن لتعرف شكلهما:

Scaffold(
appBar: AppBar(title: const Text('الرئيسية')),
drawer: const Drawer(
child: Center(child: Text('القائمة الجانبية')),
),
body: const Center(child: Text('المحتوى')),
)

بمجرد أن تضيف drawer، يضع Flutter زر القائمة (ثلاث شرطات) في AppBar تلقائياً، ويفتح القائمة عند الضغط عليه أو عند السحب من حافة الشاشة.

المشروع: شاشة صندوق الوارد

Section titled “المشروع: شاشة صندوق الوارد”
lib/inbox_screen.dart
class InboxScreen extends StatefulWidget {
const InboxScreen({super.key});
@override
State<InboxScreen> createState() => _InboxScreenState();
}
class _InboxScreenState extends State<InboxScreen> {
String _status = 'لا توجد رسائل جديدة';
void _show(String text) {
setState(() {
_status = text;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade100,
appBar: AppBar(
title: const Text('صندوق الوارد'),
centerTitle: true,
actions: [
IconButton(
icon: const Icon(Icons.search),
tooltip: 'بحث',
onPressed: () => _show('ضغطت على البحث'),
),
PopupMenuButton<String>(
onSelected: (value) => _show('اخترت: $value'),
itemBuilder: (context) => const [
PopupMenuItem(value: 'الإعدادات', child: Text('الإعدادات')),
PopupMenuItem(value: 'المساعدة', child: Text('المساعدة')),
],
),
],
),
drawer: const Drawer(
child: SafeArea(
child: Padding(
padding: EdgeInsets.all(16),
child: Text('مجلدات البريد'),
),
),
),
body: Center(
child: Text(_status, style: const TextStyle(fontSize: 18)),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => _show('رسالة جديدة فارغة'),
icon: const Icon(Icons.edit),
label: const Text('رسالة جديدة'),
),
);
}
}

كيف تعمل الشاشة:

  1. في المنتصف نص يعرض آخر شيء فعله المستخدم.
  2. أيقونة البحث تغيّر النص إلى «ضغطت على البحث».
  3. زر الثلاث نقاط يفتح قائمة، واختيار «الإعدادات» يغيّر النص إلى «اخترت: الإعدادات».
  4. زر القائمة في بداية الشريط ظهر تلقائياً لأننا أضفنا drawer.
  5. الزر العائم يكتب «رسالة جديدة فارغة».
  1. شغّل شاشة صندوق الوارد وجرّب كل زر فيها.
  2. غيّر لون الشريط العلوي بـ backgroundColor و foregroundColor.
  3. انقل الزر العائم إلى منتصف الشاشة من الأسفل.
  4. تحدٍّ: أضف عنصراً ثالثاً للقائمة المنسدلة اسمه «تصفير»، يعيد النص إلى «لا توجد رسائل جديدة».
  • وضع Scaffold داخل Scaffold: كل شاشة لها Scaffold واحد. التكرار يسبب شريطين علويين ومسافات غريبة.
  • نسيان tooltip لأزرار الأيقونات: المستخدم الذي يعتمد على قارئ الشاشة لن يعرف وظيفة الزر.
  • محتوى يختفي تحت نتوء الكاميرا: في الشاشات التي بلا AppBar، لف المحتوى بـ SafeArea.

سؤال

أين تضع أزرار البحث والإشعارات في الشريط العلوي؟

سؤال

ما وظيفة SafeArea؟

  • Scaffold مخطط جاهز للشاشة: appBar و body و floatingActionButton و bottomNavigationBar و drawer.
  • AppBar يقبل leading في البداية و actions في النهاية، مع centerTitle والألوان.
  • PopupMenuButton يجمع الإجراءات الثانوية في قائمة بثلاث نقاط.
  • زر عائم واحد للإجراء الأهم، ويتحدد مكانه بـ floatingActionButtonLocation.
  • SafeArea تحمي المحتوى في الشاشات التي بلا شريط علوي.