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

أول تطبيق Flutter: MaterialApp و Scaffold

الدرس 2 من 33 · Flutter

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

عندما تنشئ مشروعاً بـ flutter create my_app، ستجد مجلدات كثيرة. لا تخف منها، فأغلب وقتك ستقضيه في مكانين فقط:

المسار ماذا فيه؟
lib/ كود تطبيقك، وأهم ملف فيه main.dart
pubspec.yaml اسم التطبيق والمكتبات التي يستخدمها والصور والخطوط
android/ و ios/ إعدادات كل منصة، ونادراً ما تعدّلها في البداية
test/ اختبارات الكود
lib/main.dart
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),
),
);
}
}

النتيجة: شاشة بشريط علوي بنفسجي مكتوب فيه «بطاقتي»، وفي المنتصف جملة كبيرة، وفي الركن السفلي زر دائري عليه قلب. لنفهمها قطعة قطعة.

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: الشاشة الأولى التي تظهر عند فتح التطبيق.

افتراضياً، اتجاه التطبيق من اليسار لليمين. الخاصية builder تلف التطبيق كله في Directionality باتجاه TextDirection.rtl، فيصبح كل شيء من اليمين: النصوص، وزر الرجوع، وترتيب العناصر في الصفوف.

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

  • appBar: الشريط العلوي، ونستخدم له AppBar بعنوان وألوان.
  • body: محتوى الشاشة الرئيسي.
  • floatingActionButton: الزر العائم في الركن، للإجراء الأهم في الشاشة.
  • وهناك أماكن أخرى مثل drawer للقائمة الجانبية، و bottomNavigationBar لشريط التنقل السفلي.
FloatingActionButton(
onPressed: () {
debugPrint('تم الضغط على الزر');
},
child: const Icon(Icons.favorite),
)

onPressed تستقبل دالة مجهولة تُنفَّذ عند الضغط، كما تعلمت في درس الدوال بكورس Dart. و debugPrint تطبع رسالة في طرفية التطوير، وهي مفيدة للتجربة. و Icons فيها آلاف الأيقونات الجاهزة.

لاحظ const أمام Text و Icon و HomeScreen. هذه الكلمة تخبر Flutter أن هذا الجزء لن يتغير أبداً، فيبنيه مرة واحدة ويعيد استخدامه دون إعادة بنائه. النتيجة: تطبيق أسرع. ولا تحفظ أين تضعها: المحرر سيقترحها عليك بخط متعرج أزرق، وتضيفها بضغطة واحدة.

لاحظ أيضاً الفاصلة بعد آخر معامل، مثل child: Text(...),. هذه الفاصلة الأخيرة (Trailing Comma) تجعل أداة التنسيق في المحرر ترتب الكود في أسطر متداخلة واضحة. اضغط Shift + Alt + F في VS Code وشاهد الكود يترتب وحده.

  1. انسخ التطبيق إلى lib/main.dart في مشروعك وشغّله.
  2. غيّر seedColor إلى Colors.teal، ولون AppBar، واحفظ لترى Hot Reload.
  3. غيّر الأيقونة إلى Icons.star، والجملة إلى اسمك.
  4. تحدٍّ: أضف drawer: const Drawer() إلى Scaffold، ولاحظ ظهور زر القائمة في شريط العنوان تلقائياً، على اليمين لأن التطبيق عربي.
  • نسيان MaterialApp: إذا وضعت Scaffold مباشرة في runApp، ستظهر أخطاء غريبة لأن كثيراً من الـ Widgets تحتاج الإعدادات التي يوفرها MaterialApp.
  • خطأ في الأقواس: الكود متداخل جداً، وقوس ناقص واحد يكسر كل شيء. اعتمد على الفاصلة الأخيرة وأداة التنسيق.
  • نسيان الحفظ: Hot Reload يعمل عند حفظ الملف، فإذا لم تر التغيير تأكد أنك حفظت.

سؤال

أي Widget يوفر الهيكل الجاهز للشاشة بأماكن للشريط العلوي والمحتوى والزر العائم؟

سؤال

ما فائدة const أمام Widget مثل const Text('مرحباً')؟

  • كود التطبيق في lib/، والبداية من main التي تستدعي runApp.
  • MaterialApp غلاف التطبيق: العنوان والثيم والشاشة الأولى home.
  • Directionality باتجاه rtl يجعل التطبيق عربياً.
  • Scaffold هيكل الشاشة: appBar و body و floatingActionButton.
  • const للأجزاء الثابتة لأداء أفضل، والفاصلة الأخيرة لتنسيق أوضح.