أول تطبيق Flutter: MaterialApp و Scaffold
في الدرس السابق رأيت تطبيقاً صغيراً يعرض جملة في منتصف الشاشة. الآن سنبني تطبيقاً أكمل: له شريط عنوان، ولون مميز، وزر عائم، ويعرض العربية من اليمين لليسار. والأهم أنك ستفهم دور كل سطر، لأن هذا الهيكل نفسه ستبدأ به كل تطبيق تكتبه بعد اليوم.
مجلدات المشروع
Section titled “مجلدات المشروع”عندما تنشئ مشروعاً بـ flutter create my_app، ستجد مجلدات كثيرة. لا تخف منها، فأغلب وقتك ستقضيه في مكانين فقط:
| المسار | ماذا فيه؟ |
|---|---|
lib/ |
كود تطبيقك، وأهم ملف فيه main.dart |
pubspec.yaml |
اسم التطبيق والمكتبات التي يستخدمها والصور والخطوط |
android/ و ios/ |
إعدادات كل منصة، ونادراً ما تعدّلها في البداية |
test/ |
اختبارات الكود |
التطبيق الكامل
Section titled “التطبيق الكامل”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( title: 'تطبيقي الأول', debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo), ), builder: (context, child) => Directionality( textDirection: TextDirection.rtl, child: child!, ), home: const HomeScreen(), ); }}
class HomeScreen extends StatelessWidget { const HomeScreen({super.key});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('بطاقتي'), backgroundColor: Colors.indigo, foregroundColor: Colors.white, ), body: const Center( child: Text( 'أنا سارة، أتعلم Flutter', style: TextStyle(fontSize: 24), ), ), floatingActionButton: FloatingActionButton( onPressed: () { debugPrint('تم الضغط على الزر'); }, child: const Icon(Icons.favorite), ), ); }}النتيجة: شاشة بشريط علوي بنفسجي مكتوب فيه «بطاقتي»، وفي المنتصف جملة كبيرة، وفي الركن السفلي زر دائري عليه قلب. لنفهمها قطعة قطعة.
البداية: main و runApp
Section titled “البداية: main و runApp”void main() { runApp(const MyApp());}main نقطة البداية كما تعلمت في Dart. و runApp تأخذ الـ Widget الأول وتجعله جذر التطبيق كله، أي يملأ الشاشة. كل ما سيظهر بعد ذلك يتفرع منه.
MyApp: إعدادات التطبيق كله
Section titled “MyApp: إعدادات التطبيق كله”MyApp كلاس يرث من StatelessWidget، ويعيد كتابة الدالة build التي تُرجع شكله. هذا بالضبط ما تعلمته عن الوراثة و @override في كورس Dart. وستفهم معنى Stateless في درس قادم.
أما MaterialApp فهو غلاف التطبيق، ويضبط الإعدادات العامة:
title: اسم التطبيق الذي يظهر في قائمة التطبيقات المفتوحة.debugShowCheckedModeBanner: false: يخفي شريط DEBUG الأحمر من ركن الشاشة.theme: الثيم العام.ColorScheme.fromSeedتأخذ لوناً واحداً وتولّد منه مجموعة ألوان متناسقة لكل التطبيق: الأزرار والحقول والخلفيات.home: الشاشة الأولى التي تظهر عند فتح التطبيق.
جعل التطبيق عربياً
Section titled “جعل التطبيق عربياً”افتراضياً، اتجاه التطبيق من اليسار لليمين. الخاصية builder تلف التطبيق كله في Directionality باتجاه TextDirection.rtl، فيصبح كل شيء من اليمين: النصوص، وزر الرجوع، وترتيب العناصر في الصفوف.
Scaffold: هيكل الشاشة
Section titled “Scaffold: هيكل الشاشة”Scaffold معناها «السقالة»، وهي الهيكل الجاهز لأي شاشة. تخيّلها مثل ورقة امتحان مطبوعة فيها أماكن محددة مسبقاً: مكان للعنوان في الأعلى، ومساحة كبيرة للإجابة، ومكان للدرجة في الأسفل. أنت فقط تملأ الأماكن:
appBar: الشريط العلوي، ونستخدم لهAppBarبعنوان وألوان.body: محتوى الشاشة الرئيسي.floatingActionButton: الزر العائم في الركن، للإجراء الأهم في الشاشة.- وهناك أماكن أخرى مثل
drawerللقائمة الجانبية، وbottomNavigationBarلشريط التنقل السفلي.
الزر و onPressed
Section titled “الزر و onPressed”FloatingActionButton( onPressed: () { debugPrint('تم الضغط على الزر'); }, child: const Icon(Icons.favorite),)onPressed تستقبل دالة مجهولة تُنفَّذ عند الضغط، كما تعلمت في درس الدوال بكورس Dart. و debugPrint تطبع رسالة في طرفية التطوير، وهي مفيدة للتجربة. و Icons فيها آلاف الأيقونات الجاهزة.
لماذا كل هذه الـ const؟
Section titled “لماذا كل هذه الـ const؟”لاحظ const أمام Text و Icon و HomeScreen. هذه الكلمة تخبر Flutter أن هذا الجزء لن يتغير أبداً، فيبنيه مرة واحدة ويعيد استخدامه دون إعادة بنائه. النتيجة: تطبيق أسرع. ولا تحفظ أين تضعها: المحرر سيقترحها عليك بخط متعرج أزرق، وتضيفها بضغطة واحدة.
الفواصل في آخر الأسطر
Section titled “الفواصل في آخر الأسطر”لاحظ أيضاً الفاصلة بعد آخر معامل، مثل child: Text(...),. هذه الفاصلة الأخيرة (Trailing Comma) تجعل أداة التنسيق في المحرر ترتب الكود في أسطر متداخلة واضحة. اضغط Shift + Alt + F في VS Code وشاهد الكود يترتب وحده.
جرّب بنفسك
Section titled “جرّب بنفسك”- انسخ التطبيق إلى
lib/main.dartفي مشروعك وشغّله. - غيّر
seedColorإلىColors.teal، ولونAppBar، واحفظ لترى Hot Reload. - غيّر الأيقونة إلى
Icons.star، والجملة إلى اسمك. - تحدٍّ: أضف
drawer: const Drawer()إلىScaffold، ولاحظ ظهور زر القائمة في شريط العنوان تلقائياً، على اليمين لأن التطبيق عربي.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان MaterialApp: إذا وضعت
Scaffoldمباشرة فيrunApp، ستظهر أخطاء غريبة لأن كثيراً من الـ Widgets تحتاج الإعدادات التي يوفرهاMaterialApp. - خطأ في الأقواس: الكود متداخل جداً، وقوس ناقص واحد يكسر كل شيء. اعتمد على الفاصلة الأخيرة وأداة التنسيق.
- نسيان الحفظ: Hot Reload يعمل عند حفظ الملف، فإذا لم تر التغيير تأكد أنك حفظت.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
أي Widget يوفر الهيكل الجاهز للشاشة بأماكن للشريط العلوي والمحتوى والزر العائم؟
Scaffold هيكل الشاشة الواحدة، أما MaterialApp فغلاف التطبيق كله بإعداداته العامة.
سؤال
ما فائدة const أمام Widget مثل const Text('مرحباً')؟
الـ Widgets الثابتة تُنشأ مرة واحدة ويُعاد استخدامها، وهذا يجعل التطبيق أسرع.
الخلاصة
Section titled “الخلاصة”- كود التطبيق في
lib/، والبداية منmainالتي تستدعيrunApp. MaterialAppغلاف التطبيق: العنوان والثيم والشاشة الأولىhome.Directionalityباتجاهrtlيجعل التطبيق عربياً.Scaffoldهيكل الشاشة:appBarوbodyوfloatingActionButton.constللأجزاء الثابتة لأداء أفضل، والفاصلة الأخيرة لتنسيق أوضح.