إعداد Flutter والتطبيق الأول

بناء وتشغيل تطبيق مبتدئ كامل

55 دقيقة الدرس 12 من 12

المشروع التطبيقي: تطبيق بطاقة الملف الشخصي

في هذا الدرس الأخير من برنامج تعليم إعداد Flutter والتطبيق الأول، ستبني تطبيق بطاقة ملف شخصي كامل من الصفر. يجمع هذا المشروع التطبيقي كل ما تعلمته: إنشاء مشروع، تكوين pubspec.yaml، بناء شجرة عناصر الواجهة، إضافة خطوط وألوان مخصصة، استخدام إعادة التحميل السريع، تصحيح الأخطاء، والبناء للإصدار. تابع خطوة بخطوة لإنشاء تطبيق مصقول واحترافي المظهر.

ملاحظة: هذا درس عملي. افتح بيئة التطوير واتبع كل خطوة. بنهاية الدرس، سيكون لديك تطبيق بطاقة ملف شخصي يعمل بالكامل يمكنك تخصيصه بمعلوماتك الخاصة واستخدامه كعمل في محفظتك.

الخطوة 1: إنشاء المشروع

لنبدأ بإنشاء مشروع Flutter جديد باسم منظمة مخصص.

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

# إنشاء المشروع
flutter create --org com.example profile_card_app

# الانتقال إلى المشروع
cd profile_card_app

# الفتح في بيئة التطوير
code .   # VS Code
# أو
studio . # Android Studio

نظرة عامة على هيكل المشروع

بعد الإنشاء، يحتوي مشروعك على هذا الهيكل:

هيكل مجلد المشروع

profile_card_app/
├── android/          # تكوين خاص بـ Android
├── ios/              # تكوين خاص بـ iOS
├── lib/              # كود Dart الخاص بك هنا
│   └── main.dart     # نقطة دخول التطبيق
├── test/             # ملفات الاختبار
├── web/              # ملفات منصة الويب
├── pubspec.yaml      # تكوين المشروع
├── pubspec.lock      # ملف قفل التبعيات
├── analysis_options.yaml  # قواعد الفحص
└── README.md

الخطوة 2: تكوين pubspec.yaml

قبل كتابة أي كود، لنقم بتكوين تبعيات وأصول مشروعنا.

pubspec.yaml المحدّث

name: profile_card_app
description: A personal profile card app built with Flutter.
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.6
  google_fonts: ^6.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.1

flutter:
  uses-material-design: true

  assets:
    - assets/images/

إعداد مجلد الأصول

أنشئ مجلد الأصول وأضف صورة ملف شخصي نائبة:

إنشاء مجلدات الأصول

# إنشاء مجلد الأصول
mkdir -p assets/images

# أضف صورة ملف شخصي إلى assets/images/
# يمكنك استخدام أي ملف صورة باسم profile.jpg أو profile.png
# في الوقت الحالي، سنستخدم صورة نائبة من الشبكة

# تثبيت التبعيات
flutter pub get
نصيحة: في هذا الدرس، سنستخدم صورة من الشبكة حتى لا تحتاج لإضافة ملف صورة محلي. في تطبيق حقيقي، ستضع صورتك في assets/images/ وتستخدم Image.asset().

الخطوة 3: تعريف نظام الألوان والسمة

لننشئ نظام ألوان نظيف واحترافي لتطبيقنا. سنحدد سمتنا في main.dart.

lib/main.dart - نقطة دخول التطبيق والسمة

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

void main() {
  runApp(const ProfileCardApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Profile Card',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF1A73E8),
          brightness: Brightness.light,
        ),
        textTheme: GoogleFonts.poppinsTextTheme(),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF1A73E8),
          brightness: Brightness.dark,
        ),
        textTheme: GoogleFonts.poppinsTextTheme(
          ThemeData.dark().textTheme,
        ),
        useMaterial3: true,
      ),
      home: const ProfileScreen(),
    );
  }
}
ملاحظة: نستخدم ColorScheme.fromSeed() وهي طريقة Material 3 لإنشاء لوحة ألوان متناغمة من لون بذرة واحد. هذا يضمن أن جميع ألوانك تبدو جيدة معاً تلقائياً.

الخطوة 4: بناء شاشة الملف الشخصي

الآن لنبني شاشة الملف الشخصي الرئيسية مع Scaffold و AppBar وجسم قابل للتمرير.

عنصر واجهة ProfileScreen

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

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('My Profile'),
        centerTitle: true,
        backgroundColor: colorScheme.primaryContainer,
        foregroundColor: colorScheme.onPrimaryContainer,
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const SizedBox(height: 20),
            const ProfileHeader(),
            const SizedBox(height: 24),
            const ProfileInfoCard(),
            const SizedBox(height: 16),
            const SkillsCard(),
            const SizedBox(height: 16),
            const ContactCard(),
            const SizedBox(height: 20),
          ],
        ),
      ),
    );
  }
}

الخطوة 5: إنشاء رأس الملف الشخصي

يعرض رأس الملف الشخصي صورة المستخدم الرمزية والاسم والمسمى الوظيفي.

عنصر واجهة ProfileHeader

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

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Column(
      children: [
        // الصورة الرمزية للملف الشخصي
        Container(
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            border: Border.all(
              color: colorScheme.primary,
              width: 3,
            ),
            boxShadow: [
              BoxShadow(
                color: colorScheme.primary.withOpacity(0.3),
                blurRadius: 12,
                spreadRadius: 2,
              ),
            ],
          ),
          child: CircleAvatar(
            radius: 60,
            backgroundColor: colorScheme.primaryContainer,
            child: Icon(
              Icons.person,
              size: 60,
              color: colorScheme.onPrimaryContainer,
            ),
          ),
        ),
        const SizedBox(height: 16),

        // الاسم
        Text(
          'Edrees Salih',
          style: textTheme.headlineMedium?.copyWith(
            fontWeight: FontWeight.bold,
            color: colorScheme.onSurface,
          ),
        ),
        const SizedBox(height: 4),

        // المسمى الوظيفي
        Text(
          'Flutter & Web Developer',
          style: textTheme.titleMedium?.copyWith(
            color: colorScheme.onSurfaceVariant,
          ),
        ),
        const SizedBox(height: 8),

        // الموقع
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(
              Icons.location_on_outlined,
              size: 16,
              color: colorScheme.onSurfaceVariant,
            ),
            const SizedBox(width: 4),
            Text(
              'Saudi Arabia',
              style: textTheme.bodyMedium?.copyWith(
                color: colorScheme.onSurfaceVariant,
              ),
            ),
          ],
        ),
      ],
    );
  }
}

الخطوة 6: إنشاء بطاقة معلومات الملف الشخصي

تعرض هذه البطاقة نبذة مختصرة وإحصائيات رئيسية.

عنصر واجهة ProfileInfoCard

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

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // عنوان القسم
            Row(
              children: [
                Icon(Icons.info_outline, color: colorScheme.primary),
                const SizedBox(width: 8),
                Text(
                  'نبذة عني',
                  style: textTheme.titleLarge?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            const Divider(height: 24),

            // نص النبذة
            Text(
              'مطور شغوف بخبرة في Flutter و Laravel '
              'وتقنيات الويب الحديثة. أحب بناء تطبيقات '
              'جميلة وعملية تحل مشاكل حقيقية.',
              style: textTheme.bodyLarge?.copyWith(
                height: 1.6,
                color: colorScheme.onSurfaceVariant,
              ),
            ),
            const SizedBox(height: 20),

            // صف الإحصائيات
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _buildStat(context, '+5', 'سنوات خبرة'),
                _buildStat(context, '+50', 'مشروع'),
                _buildStat(context, '+10', 'دورة'),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildStat(BuildContext context, String value, String label) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Column(
      children: [
        Text(
          value,
          style: textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
            color: colorScheme.primary,
          ),
        ),
        const SizedBox(height: 4),
        Text(
          label,
          style: textTheme.bodySmall?.copyWith(
            color: colorScheme.onSurfaceVariant,
          ),
        ),
      ],
    );
  }
}

الخطوة 7: إنشاء بطاقة المهارات

تعرض بطاقة المهارات كفاءات التقنية باستخدام عناصر Chip داخل عنصر Wrap.

عنصر واجهة SkillsCard

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

  static const List<Map<String, dynamic>> skills = [
    {'name': 'Flutter', 'icon': Icons.phone_android},
    {'name': 'Dart', 'icon': Icons.code},
    {'name': 'Laravel', 'icon': Icons.web},
    {'name': 'PHP', 'icon': Icons.data_object},
    {'name': 'JavaScript', 'icon': Icons.javascript},
    {'name': 'React', 'icon': Icons.display_settings},
    {'name': 'MySQL', 'icon': Icons.storage},
    {'name': 'Git', 'icon': Icons.merge_type},
    {'name': 'Firebase', 'icon': Icons.cloud},
    {'name': 'REST API', 'icon': Icons.api},
  ];

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // عنوان القسم
            Row(
              children: [
                Icon(Icons.build_outlined, color: colorScheme.primary),
                const SizedBox(width: 8),
                Text(
                  'المهارات',
                  style: textTheme.titleLarge?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            const Divider(height: 24),

            // شرائح المهارات
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: skills.map((skill) {
                return Chip(
                  avatar: Icon(
                    skill['icon'] as IconData,
                    size: 18,
                    color: colorScheme.onSecondaryContainer,
                  ),
                  label: Text(skill['name'] as String),
                  backgroundColor: colorScheme.secondaryContainer,
                  labelStyle: TextStyle(
                    color: colorScheme.onSecondaryContainer,
                  ),
                  side: BorderSide.none,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(20),
                  ),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }
}

الخطوة 8: إنشاء بطاقة الاتصال

تعرض بطاقة الاتصال معلومات الاتصال مع أزرار تفاعلية.

عنصر واجهة ContactCard

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

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
      ),
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // عنوان القسم
            Row(
              children: [
                Icon(Icons.contact_mail_outlined, color: colorScheme.primary),
                const SizedBox(width: 8),
                Text(
                  'الاتصال',
                  style: textTheme.titleLarge?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            const Divider(height: 24),

            // عناصر الاتصال
            _buildContactItem(
              context,
              Icons.email_outlined,
              'البريد الإلكتروني',
              'edrees@example.com',
            ),
            const SizedBox(height: 12),
            _buildContactItem(
              context,
              Icons.language,
              'الموقع الإلكتروني',
              'esb1995.com',
            ),
            const SizedBox(height: 12),
            _buildContactItem(
              context,
              Icons.code,
              'GitHub',
              'github.com/edrees',
            ),
            const SizedBox(height: 20),

            // أزرار الإجراء
            Row(
              children: [
                Expanded(
                  child: FilledButton.icon(
                    onPressed: () {
                      debugPrint('Send message tapped');
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          content: Text('ميزة الرسائل قادمة قريباً!'),
                        ),
                      );
                    },
                    icon: const Icon(Icons.send),
                    label: const Text('رسالة'),
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: OutlinedButton.icon(
                    onPressed: () {
                      debugPrint('Share profile tapped');
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          content: Text('ميزة المشاركة قادمة قريباً!'),
                        ),
                      );
                    },
                    icon: const Icon(Icons.share),
                    label: const Text('مشاركة'),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildContactItem(
    BuildContext context,
    IconData icon,
    String label,
    String value,
  ) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;

    return Row(
      children: [
        Container(
          padding: const EdgeInsets.all(8),
          decoration: BoxDecoration(
            color: colorScheme.primaryContainer,
            borderRadius: BorderRadius.circular(8),
          ),
          child: Icon(
            icon,
            size: 20,
            color: colorScheme.onPrimaryContainer,
          ),
        ),
        const SizedBox(width: 12),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              label,
              style: textTheme.bodySmall?.copyWith(
                color: colorScheme.onSurfaceVariant,
              ),
            ),
            Text(
              value,
              style: textTheme.bodyMedium?.copyWith(
                fontWeight: FontWeight.w500,
              ),
            ),
          ],
        ),
      ],
    );
  }
}

الخطوة 9: استخدام إعادة التحميل السريع للتكرار

الآن بعد بناء تطبيقك، لنتدرب على استخدام إعادة التحميل السريع لإجراء تغييرات فورية.

سير عمل إعادة التحميل السريع

# شغّل التطبيق
flutter run

# قم بتغيير في الكود، مثلاً غيّر الاسم:
# 'Edrees Salih' -> 'اسمك هنا'

# اضغط 'r' في الطرفية لإعادة التحميل السريع
# أو اضغط Ctrl+S (VS Code) / Cmd+S (macOS)
# يظهر التغيير فوراً دون فقدان حالة التطبيق!

# جرّب هذه التغييرات وأعد التحميل السريع لكل واحدة:
# 1. غيّر لون البذرة: Color(0xFF1A73E8) -> Color(0xFF6750A4)
# 2. غيّر نص النبذة
# 3. أضف مهارة جديدة لقائمة المهارات
# 4. غيّر معلومات الاتصال
نصيحة: تحافظ إعادة التحميل السريع على الحالة الحالية لتطبيقك (موضع التمرير، مدخلات النماذج، إلخ). إذا أجريت تغييرات هيكلية مثل إضافة متغيرات حالة جديدة أو تغيير تسلسل شجرة العناصر، قد تحتاج لاستخدام إعادة التشغيل السريع (R) بدلاً من ذلك.

الخطوة 10: تصحيح خطأ متعمد

لنُدخل خطأ عمداً ونتدرب على تصحيحه. هذا التمرين يعلمك كيفية قراءة رسائل الخطأ وإصلاح المشاكل الشائعة.

إدخال خطأ تخطيط

// استبدل صف الإحصائيات في ProfileInfoCard بهذا الكود المعيب:
Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: [
    _buildStat(context, '+5', 'سنوات خبرة في التطوير'),
    _buildStat(context, '+50', 'مشاريع مكتملة تم تسليمها'),
    _buildStat(context, '+10', 'دورات عبر الإنترنت منشورة'),
  ],
)

// هذا يسبب تجاوز RenderFlex لأن النص الطويل
// لا يتسع في Row!

// الخطأ: A RenderFlex overflowed by 42 pixels on the right.
// The relevant error-causing widget was: Row

// الإصلاح: اجعل تسميات الإحصائيات أقصر، أو لف كل إحصائية
// في عنصر Expanded:
Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: [
    Expanded(child: _buildStat(context, '+5', 'سنوات')),
    Expanded(child: _buildStat(context, '+50', 'مشروع')),
    Expanded(child: _buildStat(context, '+10', 'دورة')),
  ],
)

التصحيح بنقاط التوقف

// أضف نقطة توقف في ProfileInfoCard.build()
// 1. انقر على الهامش بجوار: final colorScheme = ...
// 2. شغّل في وضع التصحيح (F5)
// 3. انتقل إلى شاشة الملف الشخصي
// 4. يتوقف التنفيذ عند نقطة التوقف

// في شريط التصحيح الجانبي، افحص:
// - context.widget.runtimeType
// - colorScheme.primary (شاهد اللون الفعلي)
// - textTheme.titleLarge (شاهد خصائص الخط)

// أضف تعبير مراقبة:
// MediaQuery.of(context).size.width
// هذا يُظهر لك عرض الشاشة، مفيد للتصميم المتجاوب
تحذير: تحقق دائماً من وحدة تحكم التصحيح عندما يعرض تطبيقك شاشة خطأ حمراء أو رمادية. توفر وحدة التحكم الملف الدقيق ورقم السطر ووصف ما حدث خطأ. الشاشة الحمراء في وضع التصحيح مفيدة فعلاً -- تختفي في وضع الإصدار حيث تظهر شاشة رمادية بدلاً منها.

الخطوة 11: البناء للإصدار

الآن بعد أن يعمل تطبيقك بشكل صحيح، لنبنه للإصدار.

بناء نسخة الإصدار

# أولاً، حلل الكود بحثاً عن أي مشاكل
flutter analyze

# شغّل جميع الاختبارات
flutter test

# بناء APK للإصدار (Android)
flutter build apk --split-per-abi

# بناء App Bundle لـ Play Store (Android)
flutter build appbundle

# بناء للويب
flutter build web

# بناء لـ iOS (macOS فقط)
flutter build ios

إيجاد مخرجات البناء

# ملفات APK (مقسمة حسب المعمارية):
# build/app/outputs/flutter-apk/app-arm64-v8a-release.apk   (~15 MB)
# build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk  (~12 MB)
# build/app/outputs/flutter-apk/app-x86_64-release.apk       (~15 MB)

# App Bundle:
# build/app/outputs/bundle/release/app-release.aab

# مخرجات الويب:
# build/web/index.html (والملفات المساعدة)
ملاحظة: بناء الإصدار أصغر بكثير وأسرع من بناء التصحيح. تتضمن بناءات التصحيح أدوات تصحيح وتأكيدات وخرائط مصدر تجعل ملف APK أكبر بكثير. تطبيق بطاقة ملف شخصي نموذجي مثل هذا سيكون حوالي 12-15 ميجابايت كـ APK إصدار.

الخطوة 12: ملف main.dart الكامل

إليك ملف main.dart الكامل مع جميع العناصر التي بنيناها. يمكنك نسخ هذا الملف بالكامل للحصول على التطبيق العامل:

lib/main.dart الكامل

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';

void main() {
  runApp(const ProfileCardApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Profile Card',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF1A73E8),
          brightness: Brightness.light,
        ),
        textTheme: GoogleFonts.poppinsTextTheme(),
        useMaterial3: true,
      ),
      home: const ProfileScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    return Scaffold(
      appBar: AppBar(
        title: const Text('My Profile'),
        centerTitle: true,
        backgroundColor: colorScheme.primaryContainer,
        foregroundColor: colorScheme.onPrimaryContainer,
      ),
      body: const SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            SizedBox(height: 20),
            ProfileHeader(),
            SizedBox(height: 24),
            ProfileInfoCard(),
            SizedBox(height: 16),
            SkillsCard(),
            SizedBox(height: 16),
            ContactCard(),
            SizedBox(height: 20),
          ],
        ),
      ),
    );
  }
}

// ... (جميع فئات العناصر من الخطوات 5-8 أعلاه)

أفكار للتخصيص

الآن بعد أن لديك تطبيق بطاقة ملف شخصي يعمل، إليك أفكار لجعله خاصاً بك:

أفكار للتحسين

// 1. أضف صورة ملفك الشخصي
// استبدل الأيقونة بصورة فعلية:
CircleAvatar(
  radius: 60,
  backgroundImage: AssetImage('assets/images/profile.jpg'),
)

// 2. أضف قسم خبرة مع جدول زمني
// 3. أضف عرض مشاريع مع لقطات شاشة
// 4. أضف زر تبديل الوضع الداكن
// 5. أضف حركة للصورة الرمزية
// 6. أضف روابط وسائل التواصل مع url_launcher
// 7. اجعله متجاوباً للأجهزة اللوحية والويب
// 8. أضف زر إجراء عائم للإجراءات السريعة

ما بنيته

تهانينا! في هذا الدرس التطبيقي، بنيت تطبيق Flutter كاملاً من الصفر يتضمن:

  • إنشاء المشروع مع flutter create --org
  • تكوين pubspec.yaml مع التبعيات والأصول
  • سمة Material 3 مع ColorScheme.fromSeed() و Google Fonts
  • تكوين العناصر باستخدام Scaffold و AppBar و Column و Card و Row و Wrap والمزيد
  • عناصر مخصصة مقسمة إلى مكونات قابلة لإعادة الاستخدام ومركزة
  • أيقونات وتنسيق نصوص باستخدام نظام السمات
  • تفاعل المستخدم مع الأزرار و SnackBars
  • سير عمل إعادة التحميل السريع للتكرار السريع
  • ممارسة التصحيح مع أخطاء متعمدة
  • بناءات الإصدار لـ Android و iOS والويب
نصيحة: تطبيق بطاقة الملف الشخصي هذا أساس رائع لمشروع محفظة. وسّعه بالتنقل إلى شاشات متعددة وتكامل API والتخزين المستمر لإنشاء تطبيق شخصي كامل الميزات. كل مفهوم تعلمته في هذا البرنامج التعليمي -- من إعداد المشروع إلى التصحيح إلى النشر -- سيخدمك طوال رحلتك في تطوير Flutter.

ملخص البرنامج التعليمي

على مدار هذا البرنامج التعليمي، أتقنت أساسيات إعداد Flutter والتطوير:

  • الدروس 1-4: تثبيت Flutter وتكوين بيئة التطوير وفهم هيكل المشروع
  • الدروس 5-8: العناصر الأساسية والتخطيطات والتنسيق وشجرة العناصر
  • الدرس 9: تقنيات تصحيح الأخطاء لإيجاد وإصلاح الأخطاء بكفاءة
  • الدرس 10: إدارة التبعيات والأصول مع pubspec.yaml
  • الدرس 11: أوامر Flutter CLI لكل مرحلة من مراحل التطوير
  • الدرس 12: بناء تطبيق كامل يربط كل شيء معاً

أنت الآن مستعد للانتقال إلى مواضيع Flutter الأكثر تقدماً مثل إدارة الحالة والتنقل وبناء تطبيقات إنتاج كاملة.