تخطَّ إلى المحتوى
ARAB CODEمجتمع المطورين العرب

تنظيم مشروع Flutter: هيكل مجلدات يكبر مع التطبيق دون فوضى

درسنُشر ٢٤ أكتوبر ٢٠٢٦ · 0 رد · 1 مشاهدة#flutter#dart#هيكلة#تطبيقات
قبل ساعتين

مقدمة

مشروع Flutter الجديد يبدأ بملف main.dart واحد، وسرعان ما يصبح آلاف الأسطر إن لم تنظمه. التنظيم الجيد لا يعني طبقات معقدة؛ يعني أن تعرف أين تضع كل شيء، وأن تستطيع حذف ميزة أو تعديلها دون لمس الباقي.

أهداف التعلم

  • تنظيم المشروع حسب الميزات (Feature-first).
  • الفصل بين الواجهة والمنطق والبيانات داخل كل ميزة.
  • إدارة الإعدادات والمتغيرات حسب البيئة.
  • تجهيز الترجمة والاتجاه العربي مبكراً.

1. هيكل مقترح

lib/
  main.dart
  app/
    app.dart            # MaterialApp، السمات، التوجيه
    router.dart
    theme.dart
  core/
    network/api_client.dart
    errors/failure.dart
    utils/
  features/
    topics/
      data/
        topics_api.dart
        topics_repository.dart
        models/topic.dart
      presentation/
        topics_page.dart
        topics_controller.dart
        widgets/topic_tile.dart
    auth/
      data/ …
      presentation/ …
  l10n/
    app_ar.arb
    app_en.arb
test/
  features/topics/…

كل ما يخص «المواضيع» داخل مجلد واحد. core للأشياء المشتركة فعلاً فقط؛ لا تجعله سلة مهملات.

2. النموذج والتحويل من JSON

class Topic {
  const Topic({required this.id, required this.title, required this.replyCount});

  final String id;
  final String title;
  final int replyCount;

  factory Topic.fromJson(Map<String, dynamic> json) => Topic(
        id: json['id'] as String,
        title: json['title'] as String,
        replyCount: (json['replyCount'] as num?)?.toInt() ?? 0,
      );
}

التحويل في مكان واحد يعني أن تغيير شكل الـ API يتطلب تعديل ملف واحد.

3. المستودع يخفي مصدر البيانات

class TopicsRepository {
  TopicsRepository(this._api);
  final TopicsApi _api;

  Future<List<Topic>> latest() async {
    final rows = await _api.fetchLatest();
    return rows.map(Topic.fromJson).toList(growable: false);
  }
}

الواجهة لا تعرف أن البيانات من HTTP؛ يمكنك لاحقاً إضافة تخزين مؤقت محلي دون تغيير الصفحات.

4. الإعدادات حسب البيئة

لا تضع عنوان الخادم ثابتاً في الكود. مرره وقت البناء:

flutter run --dart-define=API_BASE=https://api.example.com
const apiBase = String.fromEnvironment('API_BASE', defaultValue: 'http://10.0.2.2:3000');

ولا تضع أسراراً في التطبيق أبداً: أي مفتاح داخل ملف APK أو IPA يمكن استخراجه. الأسرار مكانها الخادم.

5. العربية والاتجاه من اليوم الأول

# pubspec.yaml
dependencies:
  flutter_localizations:
    sdk: flutter
flutter:
  generate: true
MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
  locale: const Locale('ar'),
);

مع اللغة العربية يقلب Flutter الاتجاه تلقائياً. استخدم EdgeInsetsDirectional وAlignmentDirectional بدل left/right حتى يعمل التخطيط في الاتجاهين.

تمرين عملي

  1. أنشئ ميزة topics بالهيكل أعلاه، مع مستودع يعيد بيانات وهمية.
  2. أضف ملفي ترجمة app_ar.arb وapp_en.arb لعنوان الصفحة وجرّب تبديل اللغة.
  3. اكتب اختبار وحدة لـ Topic.fromJson مع حقل replyCount مفقود.

أخطاء شائعة

  • منطق الشبكة داخل build() فيتكرر الطلب مع كل إعادة رسم.
  • ملف utils.dart ضخم يحوي كل شيء.
  • مفاتيح API داخل التطبيق.
  • EdgeInsets.only(left: …) في تطبيق عربي.

الخلاصة

نظّم حسب الميزات، وافصل الواجهة عن البيانات داخل كل ميزة، ومرر الإعدادات وقت البناء، وجهّز الترجمة والاتجاه مبكراً. ستشكر نفسك عندما يصل التطبيق إلى عشرين شاشة.

مصادر مقترحة

  • توثيق Flutter: «Guide to app architecture».
  • توثيق Flutter: «Internationalizing Flutter apps».
  • حزمة very_good_analysis لقواعد تحليل صارمة.

الرد على الموضوع

سجّل الدخول للمشاركة في النقاش.

نقاشات مشابهة