تطبيقك الأول في Flutter
إنشاء مشروع Flutter الأول
الآن بعد تثبيت Flutter وتهيئته حان الوقت لبناء تطبيقك الأول. يوفر Flutter قالب تطبيق عداد افتراضي يوضح المفاهيم الأساسية مثل الودجات وإدارة الحالة وشجرة الودجات. في هذا الدرس سننشئ ونستكشف ونعدل ونشغل هذا التطبيق خطوة بخطوة.
إنشاء مشروع Flutter جديد
flutter create my_first_app
cd my_first_app
ينشئ هذا الأمر مشروع Flutter كاملاً بهيكل مجلدات منظم. الملف الرئيسي الذي سنركز عليه هو lib/main.dart الذي يحتوي على نقطة دخول التطبيق.
my_first_app صالح لكن MyFirstApp أو my-first-app غير صالحين.فهم تطبيق العداد الافتراضي
عند فتح lib/main.dart سترى تطبيق العداد الافتراضي. دعنا نحلله قطعة بقطعة لفهم كل جزء.
دالة main()
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
دالة main() هي نقطة دخول كل تطبيق Dart. دالة runApp() تأخذ ودجت وتجعلها جذر شجرة الودجات. كل تطبيق Flutter يبدأ من هنا.
MaterialApp -- الأساس
ودجت MaterialApp هي الودجت العليا التي تغلف تطبيقك بالكامل. توفر تصميم Material Design والتنقل والسمات والعديد من الميزات الأخرى جاهزة للاستخدام.
MyApp كـ StatelessWidget
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
الخصائص الرئيسية لـ MaterialApp:
title-- عنوان التطبيق المعروض في محول المهامtheme-- يحدد المظهر المرئي (الألوان والخطوط والأشكال)home-- الودجت المعروضة عند بدء التطبيقuseMaterial3: true-- يفعل أحدث تصميم Material Design 3
ColorScheme.fromSeed() ينشئ لوحة ألوان كاملة من لون بذرة واحد. هذه ميزة في Material Design 3 تضمن ألواناً متسقة وسهلة الوصول في تطبيقك.Scaffold -- هيكل الصفحة
ودجت Scaffold توفر هيكل التخطيط المرئي الأساسي لصفحة Material Design. تمنحك شريط تطبيق ومنطقة محتوى وزر عائم وأدراج جانبية والمزيد.
MyHomePage كـ StatefulWidget
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
هذا StatefulWidget مما يعني أنه يمكنه الاحتفاظ بالحالة وتغييرها بمرور الوقت. دالة createState() تعيد كائن State المرتبط حيث توجد البيانات القابلة للتغيير ومنطق البناء.
State و setState() ودالة Build
فئة State هي حيث يحدث السحر. تحتفظ بالبيانات القابلة للتغيير (_counter) وتحدد كيفية بناء وإعادة بناء واجهة المستخدم عند تغير الحالة.
فئة State
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'\$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
setState() عند تغيير متغيرات الحالة. بدونها لن يعرف Flutter أن واجهة المستخدم تحتاج إلى إعادة بناء ولن تظهر تغييراتك على الشاشة. لا تعدل متغيرات الحالة خارج setState() في StatefulWidget.فهم الودجات الرئيسية
دعنا نفحص كل ودجت مستخدمة في تطبيق العداد:
AppBar
ودجت AppBar تنشئ شريط أدوات أعلى الشاشة. تحتوي عادة على عنوان وأيقونات تنقل وأزرار إجراءات.
خصائص AppBar
AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
// خصائص اختيارية:
// leading: IconButton(icon: Icon(Icons.menu), onPressed: () {}),
// actions: [IconButton(icon: Icon(Icons.search), onPressed: () {})],
// centerTitle: true,
// elevation: 4.0,
)
Center و Column والتخطيط
ودجت Center تركز ابنها في وسطها. Column ترتب أبناءها عمودياً. معاً يضعان المحتوى في منتصف الشاشة مكدساً من الأعلى للأسفل.
ودجت Text
ودجت Text تعرض سلسلة نصية بنمط واحد. يمكنك تخصيصها بمعامل style.
أمثلة ودجت Text
// نص بسيط
const Text('مرحباً Flutter!')
// نص منسق
Text(
'العداد: \$_counter',
style: Theme.of(context).textTheme.headlineMedium,
)
// نص بتنسيق مخصص
Text(
'نص مخصص',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
FloatingActionButton
زر الإجراء العائم (FloatingActionButton أو FAB) هو زر دائري يطفو فوق المحتوى. يُستخدم عادة للإجراء الرئيسي على الشاشة.
خصائص FloatingActionButton
FloatingActionButton(
onPressed: _incrementCounter, // الدالة المستدعاة عند النقر
tooltip: 'Increment', // يظهر عند الضغط المطول
child: const Icon(Icons.add), // الأيقونة المعروضة
// خصائص اختيارية:
// backgroundColor: Colors.green,
// foregroundColor: Colors.white,
// mini: true, // نسخة أصغر
)
تعديل تطبيق العداد
الآن دعنا نخصص التطبيق. سنغير الألوان ونضيف زر إنقاص ونعدل النص.
تغيير مخطط الألوان
ألوان سمة مخصصة
MaterialApp(
title: 'My Counter App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
useMaterial3: true,
),
home: const MyHomePage(title: 'My Counter App'),
)
إضافة زر إنقاص
دعنا نضيف زراً ثانياً لإنقاص العداد. سنضيف أيضاً زر إعادة تعيين.
State معدلة بإجراءات متعددة
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
void _resetCounter() {
setState(() {
_counter = 0;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _resetCounter,
tooltip: 'Reset',
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('قيمة العداد:'),
Text(
'\$_counter',
style: Theme.of(context).textTheme.displayMedium?.copyWith(
color: _counter >= 0 ? Colors.teal : Colors.red,
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: _decrementCounter,
icon: const Icon(Icons.remove),
label: const Text('إنقاص'),
),
const SizedBox(width: 16),
ElevatedButton.icon(
onPressed: _incrementCounter,
icon: const Icon(Icons.add),
label: const Text('زيادة'),
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
SizedBox تُستخدم بشكل شائع لإضافة مسافات بين الودجات. SizedBox(height: 20) تضيف 20 بكسل منطقي من المسافة العمودية و SizedBox(width: 16) تضيف مسافة أفقية.تشغيل التطبيق
هناك عدة طرق لتشغيل تطبيق Flutter:
استخدام الطرفية
التشغيل من سطر الأوامر
# التشغيل على الجهاز الافتراضي
flutter run
# التشغيل على جهاز محدد
flutter run -d chrome # متصفح الويب
flutter run -d emulator-5554 # محاكي Android
flutter run -d iPhone # محاكي iOS
# عرض الأجهزة المتاحة أولاً
flutter devices
استخدام VS Code
في VS Code يمكنك الضغط على F5 أو الذهاب إلى Run > Start Debugging. تأكد من اختيار جهاز في شريط الحالة السفلي. إضافة Flutter توفر محدد أجهزة مريح.
استخدام Android Studio
في Android Studio اختر جهازاً من القائمة المنسدلة في شريط الأدوات ثم انقر على زر Run الأخضر أو اضغط Shift + F10.
فهم شجرة الودجات
كل تطبيق Flutter مبني من شجرة ودجات. لتطبيق العداد تبدو شجرة الودجات هكذا:
هيكل شجرة الودجات
MaterialApp
> MyHomePage (StatefulWidget)
> Scaffold
> AppBar
> Text (العنوان)
> Center (المحتوى)
> Column
> Text ('قيمة العداد:')
> Text (_counter)
> SizedBox
> Row
> ElevatedButton (إنقاص)
> SizedBox
> ElevatedButton (زيادة)
> FloatingActionButton
> Icon (إضافة)
فهم شجرة الودجات أساسي. كل عنصر مرئي على الشاشة هو ودجت والودجات تتركب معاً في تسلسل هرمي شجري. عند استدعاء setState() يعيد Flutter بناء الجزء المتأثر من الشجرة بكفاءة.
إضافة تنسيق مخصص
دعنا نحسن تطبيقنا ببعض التنسيق الإضافي ليكون أكثر جاذبية بصرياً.
محتوى محسن بتنسيق مخصص
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context).colorScheme.surface,
],
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.touch_app,
size: 48,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 16),
const Text(
'انقر الأزرار لتغيير العداد',
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 24),
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Text(
'\$_counter',
style: Theme.of(context).textTheme.displayLarge,
),
),
],
),
),
),
أخطاء المبتدئين الشائعة
إليك الأخطاء الشائعة التي يرتكبها مطورو Flutter الجدد وكيفية تجنبها:
- نسيان مُنشئات
const: استخدمconstللودجات التي لا تتغير أبداً لتحسين الأداء - تعديل الحالة بدون
setState(): لن يتم تحديث واجهة المستخدم ما لم تغلف التغييرات فيsetState() - نسيان الفواصل: Dart تستخدم الفواصل اللاحقة بكثرة -- أضفها بعد كل معامل للحصول على تنسيق أفضل
- نسيان الاستيراد: ودجات Material تتطلب
import 'package:flutter/material.dart';
setState(). يجب أن يقوم الاستدعاء المُمرر إلى setState() فقط بتحديث متغيرات الحالة. نفذ المنطق أولاً ثم استدعِ setState() لتحديث واجهة المستخدم.ملخص
في هذا الدرس تعلمت كيفية:
- إنشاء مشروع Flutter جديد بأمر
flutter create - فهم هيكل تطبيق العداد الافتراضي
- استخدام
MaterialAppوScaffoldوAppBarوFloatingActionButton - العمل مع
StatefulWidgetوsetState() - تعديل التطبيق بتغيير الألوان وإضافة أزرار وتخصيص النص
- تشغيل التطبيق على أجهزة مختلفة باستخدام الطرفية أو بيئة التطوير
- فهم تسلسل شجرة الودجات الهرمي
تمرين عملي
عدل تطبيق العداد ليتضمن: (1) عنوان يعرض “عدادي المخصص” في شريط التطبيق، (2) قيمة العداد تغير لونها بناءً على ما إذا كانت موجبة (أخضر) أو سالبة (أحمر) أو صفر (رمادي)، (3) ثلاثة أزرار: زيادة بـ 1 وإنقاص بـ 1 وزيادة بـ 5، (4) زر إعادة تعيين في إجراءات شريط التطبيق. اختبر التطبيق على جهازك المفضل أو المحاكي.