📘 كورس فلاتر
✨ كورس كامل باللغة العربية · مجاني

اتعلّم Flutter من الصفر
واشتغل مبرمج موبايل محترف

منهج متكامل من 12 وحدة، بياخدك خطوة بخطوة من كتابة أول سطر كود، لحد بناء ونشر تطبيق حقيقي على Google Play. مع أكواد كاملة، تمارين، وشرح مبسّط.

12وحدة
+40مثال كود
4مشاريع
100%بالعربي
0

مقدمة وتهيئة بيئة العمل

إيه هو فلاتر؟ وليه نتعلّمه؟ وإزاي نجهّز الجهاز؟

Flutter هو إطار عمل (Framework) مفتوح المصدر من تطوير شركة Google، بيخليك تبني تطبيق واحد يشتغل على Android وiOS والويب وسطح المكتب من نفس الكود. ده معناه إنك تتعلم لغة واحدة وتبني لكل الأجهزة.

ليه فلاتر بالذات؟

  • كود واحد لكل المنصات: بدل ما تتعلم Kotlin للأندرويد و Swift للـ iOS، تكتب مرة واحدة.
  • سريع: ميزة Hot Reload بتخليك تشوف أي تعديل في التطبيق فورًا بدون ما تعيد تشغيله.
  • واجهات جميلة: فيه مكتبة ضخمة من الجاهز (Widgets) + الدعم الرسمي لتصميمات Material 3.
💡 ليه Dart؟ فلاتر مكتوب بلغة Dart، وهي لغة حديثة وسهلة، شبهها شبه JavaScript وJava. مش لازم تكون محترف فيها — هنشرحها من الصفر في الوحدة الجاية.

تهيئة بيئة العمل

الأداةالوظيفةملاحظة
Flutter SDKقلب فلاتر، فيه أدوات البناء والتشغيلحمّله من flutter.dev
Android Studioمحرّر أكواد + Android Emulatorبيجي معاه الإيمولاتر
VS Codeمحرّر خفيف وسريع (اختياري لكن مُفضّل)نزّل إضافة Flutter
Gitلحفظ مشاريعك ومزامنتهامهم جدًا للمستقبل
✅ اختبر تثبيتك افتح موجة الأوامر (Terminal) واكتب الأمر:
flutter doctor — لو طلعت علّمات صح خضراء يبقى كل حاجة تمام.
🎯 تمرين
نزّل Flutter SDK وشغّل flutter doctor، وحلّ أي مشكلة بسيطة يظهرها، واتأكد إن الإيمولاتر بيشتغل.
1

أساسيات لغة Dart

المتغيرات · الأنواع · الشروط · الحلقات · الدوال

قبل فلاتر، محتاج تفهم Dart. متخافش، دي الأساسيات اللي هتستعملها طول الوقت.

المتغيرات (Variables)

main.dart
// المتغير بيحفظ قيمة في الذاكرة
var name = 'أحمد';        // النوع بيستنتج تلقائيًا
String city = 'القاهرة';     // نص
int age = 25;               // رقم صحيح
double height = 1.75;        // رقم عشري
bool isStudent = true;      // صح / غلط

final pi = 3.14;             // ثابت بعد التعريف
const gravity = 9.8;         // ثابت من الأول
💡 المفتاح الذهبي استخدم final أو const لو القيمة مش هتتغير — ده بيسرّع تطبيقك. وnull safety في Dart معناها إن المتغير مش بياخد null غير لما تقوله String?.

الشروط (Conditions)

int score = 85;

if (score >= 90) {
  print('ممتاز');
} else if (score >= 75) {
  print('جيد جدًا');
} else {
  print('محتاج تحسّن');
}

// الاختصار الثلاثي
var result = score >= 50 ? 'ناجح' : 'راسب';

الحلقات (Loops)

for (int i = 1; i <= 5; i++) {
  print('الرقم: $i');          // $i بتحط قيمة المتغير جوه النص
}

// لف على عناصر قائمة
var langs = ['Dart', 'Flutter', 'Python'];
for (var lang in langs) {
  print(lang);
}

الدوال (Functions)

// دالة بسيطة
int add(int a, int b) {
  return a + b;
}

// Arrow function (لو سطر واحد)
int multiply(int a, int b) => a * b;

void greet(String name, {String greeting = 'أهلاً'}) {
  print('$greeting $name');   // { } = معامل اختياري
}

void main() {
  print(add(3, 4));          // 7
  greet('سارة');              // أهلاً سارة
}
🎯 تمرين
اكتب دالة بتاخد مصفوفة أرقام وترجع أكبر رقم فيها.
👁️ اعرض الحل
int largest(List<int> nums) {
  var max = nums[0];
  for (var n in nums) {
    if (n > max) max = n;
  }
  return max;
}
❓ سؤال سريع: إيه الفرق بين final و const؟
2

Dart متقدّم: البرمجة الكائنية

Classes · Objects · Inheritance · Collections · Null Safety

الكلاسات والكائنات (Classes & Objects)

class User {
  final String name;
  int age;

  // Constructor
  User({required this.name, required this.age});

  // Method
  String describe() => '$name عمره $age سنة';
}

void main() {
  var user = User(name: 'منى', age: 22);
  print(user.describe());   // منى عمره 22 سنة
}

القوائم والخَرائط (Collections)

// List = قائمة مرتبة
List<String> fruits = ['تفاح', 'موز', 'عنب'];
fruits.add('مانجو');          // ضيف عنصر

// Map = مفتاح → قيمة
Map<String, int> scores = {'علي': 90, 'سارة': 95};
print(scores['علي']);        // 90

// Set = مجموعة بدون تكرار
Set<int> ids = {1, 2, 2, 3};   // الطول = 3 (الـ 2 مرة واحدة)

Null Safety

String? nickname;               // قد يكون null
print(nickname?.length);        // ? = نفّذ لو مش null
print(nickname ?? 'بدون اسم'); // ?? = القيمة البديلة
💡 دايمًا استخدم required في المُنشئ (Constructor) بيخلي المتغير إلزامي، فبيمنعك من أخطاء النسيان — ودي حاجة هتشوفها كتير في كود فلاتر.
🎯 تمرين
اعمل كلاس Product فيه اسم وسعر وكمية، ودالة تحسب السعر الكلي (السعر × الكمية).
👁️ اعرض الحل
class Product {
  final String name;
  final double price;
  final int quantity;

  Product({required this.name, required this.price, required this.quantity});

  double total() => price * quantity;
}
❓ إيه نتيجة {1, 2, 2, 3}.length؟
3

أول تطبيق Flutter

بنية المشروع · main() · MaterialApp · Hot Reload

إنشاء المشروع

# في الـ Terminal، اكتب:
flutter create my_first_app
cd my_first_app
flutter run

هيكل المشروع

المسارالوظيفة
lib/main.dartالكود الأساسي — هنا بتكتب كل حاجة تقريبًا
pubspec.yamlملف الإعدادات + المكتبات (packages)
android/ و ios/ملفات المنصات (مش بتلمسها كتير)

أول كود

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: 'أول تطبيق',
      theme: ThemeData(useMaterial3: true),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('أهلاً بيك 👋')),
      body: const Center(
        child: Text('أول تطبيق ليا بفلاتر!', style: TextStyle(fontSize: 22)),
      ),
    );
  }
}
💡 Hot Reload = صاحبك المفضل بعد ما تشغّل التطبيق، عدّل أي حاجة واعمل حفظ — هتشوف التغيير فورًا على الشاشة بدون إعادة تشغيل.
🎯 تمرين
غيّر النص والألوان، وضع Icon جوه الـ body بدل النص، وجرّب الـ Hot Reload.
4

الواجهات والـ Widgets

Stateless vs Stateful · Text · Container · Buttons · Images

كل حاجة في فلاتر هي Widget! النص widget، الزر widget، وحتى الشاشة نفسها widget. في نوعين رئيسيين:

النوعمتى تستخدمه
StatelessWidgetلما الواجهة ثابتة ومش بتتغير
StatefulWidgetلما في بيانات بتتغير (عداد، إدخال مستخدم)

نصوص وتنسيق

Text(
  'هذا نص منسّق',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Colors.blue,
    letterSpacing: 1.2,
  ),
)

الحاوية Container

Container(
  width: 200,
  height: 100,
  padding: const EdgeInsets.all(16),
  decoration: BoxDecoration(
    color: Colors.blue.shade100,
    borderRadius: BorderRadius.circular(16),
    boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
  ),
  child: const Text('أنا جوه الحاوية'),
)

الأزرار

ElevatedButton(
  onPressed: () {
    print('تم الضغط!');
  },
  child: const Text('اضغط هنا'),
)

FilledButton.icon(
  onPressed: () {},
  icon: const Icon(Icons.add),
  label: const Text('إضافة'),
)

الصور

// صورة من الإنترنت
Image.network('https://picsum.photos/200')

// صورة من ملفات المشروع (assets)
Image.asset('assets/logo.png')
⚠️ الصور من files لو هتستخدم Image.asset، لازم تسجّل مجلد assets في ملف pubspec.yaml الأول، وإلا هيديك خطأ.

مثال StatefulWidget (عدّاد)

class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});
  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('العدد: $count', style: const TextStyle(fontSize: 30)),
            ElevatedButton(
              onPressed: () {
                setState(() => count++);   // 👈 بيعيد بناء الواجهة
              },
              child: const Text('+1'),
            ),
          ],
        ),
      ),
    );
  }
}
💡 setState جوهرة فلاتر أي تغيير في البيانات لازم يلف داخل setState() عشان فلاتر يعرف يعيد رسم الواجهة.
🎯 تمرين
اعمل تطبيق عدّاد فيه زرار للزيادة وزرار للتصفير، وبيّن العدد بلون مختلف.
👁️ فكرة الحل ضيف متغيرين حالة: count، واعمل زرارين بكل واحد setState بيعدّل القيمة.
5

التخطيطات (Layout)

Row · Column · Stack · ListView · Expanded
Widgetالوظيفة
Columnيرصّ العناصر رأسيًا (فوق بعض)
Rowيرصّ العناصر أفقيًا (جنب بعض)
Stackيضع العناصر فوق بعض (طبقات)
ListViewقائمة قابلة للتمرير، مثالية للبيانات الكتيرة
Expandedيخلي عنصر ياخد المساحة المتاحة
Column(
  children: [
    Text('العنصر الأول'),
    Text('العنصر الثاني'),
  ],
)

Row(
  children: [
    Expanded(child: Text('نص طويل')),
    Icon(Icons.star),
  ],
)

// قائمة كاملة قابلة للتمرير
ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(child: Text('${index + 1}')),
      title: Text('العنصر رقم ${index + 1}'),
    );
  },
)

بطاقة لائقة (Card)

Card(
  child: ListTile(
    leading: const Icon(Icons.person),
    title: const Text('اسم المستخدم'),
    subtitle: const Text('مطور فلاتر'),
    trailing: const Icon(Icons.arrow_forward),
  ),
)
✅ قاعدة سريعة «حطّ في بالك محور أفقي ورأسي»: Column رأسي، Row أفقي. ولو عندك عناصر كتير في العمود، حطّه جوه SingleChildScrollView عشان مايحصلش Overflow.
🎯 تمرين
اعمل شاشة فيها Row فيه صورة ومعلومات، في إطار Card — كأنه بروفايل مستخدم.
7

إدارة الحالة (State)

setState · lifting state up · TextEditingController

الحالة (State) هي أي بيانات بتتغير في تطبيقك. في البداية، ابدأ بـ setState وافهمه كويس قبل ما تدخل في مكتبات أكبر.

التعامل مع إدخال المستخدم

class _MyFormState extends State<MyForm> {
  final _controller = TextEditingController();
  String savedName = '';

  @override
  void dispose() {
    _controller.dispose();          // 👈 مهم لتحرير الذاكرة
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: const InputDecoration(labelText: 'اكتب اسمك'),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() => savedName = _controller.text);
            },
            child: const Text('حفظ'),
          ),
          Text('مرحبًا: $savedName'),
        ],
      ),
    );
  }
}
⚠️ لا تنسى dispose() أي TextEditingController أو AnimationController اعمل له dispose في النهاية، وإلا يسبب تسريب ذاكرة.
🎯 تمرين
اعمل تطبيق «To-Do» بسيط: حقل إدخال + زرار إضافة + قائمة مهام، واستخدم ListView لعرض المهام.
8

البرمجة غير المتزامنة و APIs

Future · async/await · HTTP · JSON · FutureBuilder

التطبيقات الحقيقية بتحتاج تجيب بيانات من الإنترنت. ده عملية بتاخد وقت، ولهذا نستخدم async/await.

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<List<Post>> fetchPosts() async {
  final res = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
  );

  if (res.statusCode == 200) {
    final data = json.decode(res.body) as List;
    return data.map((e) => Post.fromJson(e)).toList();
  } else {
    throw Exception('فشل التحميل');
  }
}

الكلاس Post (JSON Model)

class Post {
  final int id;
  final String title;

  Post({required this.id, required this.title});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(id: json['id'], title: json['title']);
  }
}

استخدام FutureBuilder في الواجهة

FutureBuilder<List<Post>>(
  future: fetchPosts(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const CircularProgressIndicator();
    }
    if (snapshot.hasError) {
      return Text('خطأ: ${snapshot.error}');
    }
    final posts = snapshot.data!;
    return ListView.builder(
      itemCount: posts.length,
      itemBuilder: (c, i) => ListTile(title: Text(posts[i].title)),
    );
  },
)
💡 مشروع صغير استخدم API مجاني زي jsonplaceholder.typicode.com أو dummyjson.com للتجربة بدون مفاتيح.
🎯 تمرين
اجلب قائمة منتجات من dummyjson.com/products واعرضها في ListView مع صورة واسم وسعر.
9

التخزين المحلي (Local Storage)

Shared Preferences · Hive · الملفات
الأداةالاستخدام المناسب
SharedPreferencesبيانات صغيرة: إعدادات، تفضيلات، جلسة المستخدم
Hiveقوائم وبيانات منظّمة بسرعة ودون تعقيد
sqfliteقواعد بيانات كبيرة واستعلامات معقدة
import 'package:shared_preferences/shared_preferences.dart';

// حفظ
Future<void> saveName(String name) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('name', name);
}

// قراءة
Future<String?> readName() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('name');
}
🎯 تمرين
خزّن الوضع الليلي (Dark Mode) في SharedPreferences، وخلي التطبيق يفضل على حالته لما يقفل ويفتح تاني.
10

إدارة الحالة المتقدمة

Provider · Bloc/Cubit · متى تستخدم إيه

لما التطبيق يكبر، setState لوحده مابيكفيش. هنا تيجي مكتبات إدارة الحالة.

المكتبةالمستوىالمناسبة
Providerمتوسطسهلة وممتازة للبداية والمشاريع المتوسطة
Bloc / Cubitمتقدّمالمشاريع الكبيرة، فصل منطق واضح، مستخدمة في الشركات
Riverpodمتقدّمبديل حديث لـ Provider وأقوى بأمان الأنواع

مثال Provider

// 1) الموديل
class CounterModel extends ChangeNotifier {
  int value = 0;
  void increment() {
    value++;
    notifyListeners();      // 👈 إعلام الواجهات بالتغيير
  }
}

// 2) توفير الموديل للتطبيق
ChangeNotifierProvider(
  create: (_) => CounterModel(),
  child: MaterialApp(home: HomeScreen()),
)

// 3) قراءة القيمة في الواجهة
final counter = context.watch<CounterModel>();
return Text('${counter.value}');
✅ نصيحتي متدخلش على Bloc من أول يوم. اتقن setState و Provider الأول، وبعدها انتقل لـ Bloc/Cubit بثقة.
🎯 تمرين
حوّل تطبيق الـ To-Do من الوحدة 7 ليستخدم Provider بدل setState.
11

مشروع كامل + النشر

تطبيق الطقس أو متجر مصغّر، ثم النشر على Google Play

هنجمع كل حاجة اتعلمناها في مشروع واحد: تطبيق طقس.

خريطة المشروع

  1. الواجهة: شاشة بحث + شاشة نتائج بفيها درجة الحرارة والرطوبة.
  2. API: استخدم open-meteo.com (مجاني بدون مفتاح) لجلب الطقس.
  3. الحالة: Provider لإدارة حالة التحميل والنتيجة والخطأ.
  4. التخزين: احفظ آخر مدينة بحث المستخدم عنها.
  5. التنسيق: Dark Mode جميل وأيقونات مناسبة.

خطوات النشر على Google Play

  1. عدّل pubspec.yaml: اسم التطبيق + رقم الإصدار.
  2. غيّر الأيقونة والاسم من android/app/.
  3. ابنِ نسخة الإصدار: flutter build appbundle --release
  4. اعمل حساب مطوّر على Google Play (رسوم مرة واحدة).
  5. ارفع ملف .aab واملأ بيانات المتجر.
⚠️ قبل النشر اختبر التطبيق على جهاز حقيقي، وتأكد من الأذونات والإعدادات في AndroidManifest.xml، وراجع سياسة الخصوصية لو التطبيق بيجمع بيانات.
✦

خارطة طريق بعد الكورس

عشان توصل لمستوى الشغل الاحترافي
  • اتقن Git & GitHub: ارفع كل مشاريعك، ده ملفك الشخصي.
  • Clean Architecture: تنظيم المشاريع الكبيرة.
  • Firebase: تسجيل دخول + قاعدة بيانات سحابية + إشعارات.
  • اختبار الوحدات (Testing): يفرقك عن غيرك.
  • 3 مشاريع Portfolio: متجر، تواصل اجتماعي، وأداة مفيدة.
  • شارك في المجتمع: GitHub و StackOverflow ومجتمعات فلاتر العربية.
🌟 الخلاصة مفيش سر — بس الاستمرارية والمشاريع الحقيقية. اتعلّم 30 دقيقة يوميًا وطبّق بمشروع، وبعد 3 شهور هتكون شخص تاني.
كورس فلاتر عربي · صُمّم خصيصًا لك ❤️
للمزيد من المصادر: الوثائق الرسمية لفلاتر