# 26 — نظام التصميم الثابت (الهوية البصرية لتطبيقات فلاتر) > **هذا المستند مُلزِم** ويكمّل [23-flutter-conventions](23-flutter-conventions.md): ذاك قانون *الكود*، وهذا قانون *الشكل*. > الهدف: كل مستأجر يأخذ تطبيقاً يبدو مصمَّماً له، بينما هو نفس النظام حرفياً — المتغيّر لونه ولوغوه فقط. آخر تحديث: 2026-07-19. ذو صلة: [22 §1.5](22-full-product-roadmap.md) (الطبقات) · [06](06-tenant-model.md) (المستأجرون). --- ## 0. العقد الحاكم: الثابت مقابل المتغيّر | **ثابت عبر كل المستأجرين والطبقات** | **متغيّر لكل مستأجر (من BuildConfig — يولّده N3)** | |---|---| | الخطوط (عربي + إنجليزي + أرقام) | اللون البذرة (seed) الأساسي والثانوي | | المسافات والزوايا والظلال والحركة | اللوغو والأيقونة والـsplash | | بنية الشاشات (TripzScaffold) وعدة المكونات | اسم التطبيق | | سلوك الليلي/النهاري و RTL/LTR | — | | فلسفة التصميم وأنماط الحالات | — | **القاعدة**: أي شيء غير الأعمدة الثلاثة (لون · لوغو · اسم) يتغيّر بين مستأجرين = خطأ يُصحَّح. السبب تشغيلي قبل أن يكون جمالياً: نظام واحد ثابت = شاشة تُبنى مرة وتخدم كل المستأجرين، وShorebird يرقّعها للجميع دفعة واحدة. --- ## 1. الخطوط — قرار 2026-07-19 المالك طلب «خط الآيفون». خط آبل هو **SF Pro** وهو مملوك لآبل ولا يجوز تضمينه قانونياً في تطبيق أندرويد. البديل المطابق المعتمد عالمياً من Google Fonts: | الدور | الخط | لماذا | |---|---|---| | إنجليزي + أرقام | **Inter** | أقرب مطابق حرّ لـSF Pro (نفس الروح الهندسية)، أوزان كاملة، أرقام tabular للعدّادات والأسعار | | عربي | **IBM Plex Sans Arabic** | النظير العربي الأقرب لروح SF Arabic: حديث، محايد، أوزان كاملة، ممتاز في الأحجام الصغيرة | **قواعد لا تُخالَف:** - الخطوط **تُضمَّن ملفاتٍ محلية** في `assets/fonts/` (نفس الملفات في الراكب والسائق). حزمة `google_fonts` بجلبها الشبكي **ممنوعة** — اتصال سوريا لا يُراهَن عليه، والشكل يجب أن يكون حتمياً من أول إقلاع. - الأوزان المضمّنة: Regular · Medium · SemiBold · Bold لكل خط (٨ ملفات). لا أوزان أخرى. - الأسعار والعدّادات والأرقام الحيّة تستعمل **Inter بأرقام tabular** (`FontFeature.tabularFigures`) كي لا «ترقص» الأعمدة عند تغيّر القيمة — هذا يُغني عن خط `digit` القديم. - خطوط التطبيق القديم (`mohanad` · `josefin` · `digit`) **متقاعدة** من التصميم الجديد. ملفاتها تبقى في المستودع حتى يؤكد المالك حذفها. *(هذا يُحدّث docs/23 §7 الذي كان يعتبرها هوية قائمة.)* - تبديل الخط مستقبلاً = تغيير سطرين في `typography.dart` — لا قرار مبعثر. --- ## 2. الألوان والثيم - `ColorScheme.fromSeed` من لون المستأجر (`BuildConfig.primaryColor`) — نسختان light وdark تُشتقّان آلياً من نفس البذرة. **لا لون hex مكتوب في أي شاشة.** - الأدوار الدلالية غير الموجودة في ColorScheme تعيش في `ThemeExtension` واحد اسمه `TripzColors`: `success` · `warning` · `danger` · `info` · `surfaceRaised` · ألوان طبقات الخريطة. الشاشة تقول `context.tripzColors.success` ولا تقرّر درجة اللون بنفسها. - **الليلي ليس أسود صرفاً**: سطوح متدرّجة من الرمادي الداكن (elevation بالسطوع لا بالظل). الخريطة تبدّل ستايلها مع الثيم — خريطة نهارية في وضع ليلي تكسر الغرض. - تباين النص على خلفيته **≥ 4.5:1** دائماً. البذرة مهما كانت فاتحة، `fromSeed` يضمن السطوح — والفحص جزء من قائمة تحقق الشاشة (§9). ## 3. الرموز الثابتة (tokens) — `core/design/tokens.dart` كلها `const` في ملف واحد. الشاشة **لا تخترع رقماً**: | المجموعة | السلّم | |---|---| | المسافات | مضاعفات 4: `4 · 8 · 12 · 16 · 24 · 32 · 48` — حشوة الصفحة الافتراضية **16** | | الزوايا | `8` (حقول/شارات) · `12` (أزرار/بطاقات) · `24` (bottom sheets) · دائري كامل (avatars) | | الحركة | `120ms` (لمسة) · `240ms` (انتقال ضمن الشاشة) · `400ms` (sheet/صفحة) — منحنى `easeOutCubic` موحّد | | الأيقونات | `20 · 24 · 32` — عائلة واحدة: Material Symbols Rounded | | الارتفاع | زر CTA `52` · حقل إدخال `52` · هدف لمس أدنى `48` | ## 4. الليلي/النهاري واللغة والاتجاه - **الثيم**: الافتراضي `ThemeMode.system`، والمستخدم يستطيع تثبيت light/dark من الإعدادات. الاختيار يُحفظ محلياً (SharedPreferences — ليس سراً) عبر `SettingsCubit` في `features/settings/`. - **اللغة**: الافتراضي عربي، والإنجليزية مدعومة. الاختيار محفوظ بنفس الطريقة. - **الاتجاه يتبع اللغة آلياً** — MaterialApp يفعلها وحده من الـlocale. ⚠️ الموجود الآن في `app.dart` يفرض `Directionality.rtl` فوق كل شيء — **يُحذف**: مع الإنجليزية يجب أن ينقلب التطبيق LTR كاملاً. - **الخط يتبع اللغة**: الـTextTheme يُبنى حسب الـlocale — عربي ⇒ IBM Plex Sans Arabic والاحتياط Inter، إنجليزي ⇒ Inter والاحتياط Plex (كي لا تنكسر الكلمة العربية داخل جملة إنجليزية والعكس). - **الترجمة**: ARB + `gen_l10n` القياسية. لا نص مرئي داخل widget — يمرّ بـ`context.l10n` (يفعّل قاعدة docs/23 §2 التي كانت بلا أداة). - دالة بناء الثيم النهائية: `buildTheme(brightness, locale)` — المدخلان الوحيدان. ## 5. الأرقام والتاريخ والعملة - **الأرقام لاتينية (1 2 3) دائماً** في اللغتين — عرف المنطقة في التطبيقات، وتناسق الأسعار والعدادات أهم من الأرقام الهندية. - العملة ورمزها وموضعها من **country pack** المستأجر عبر `intl` — لا تنسيق يدوي بـ`toString`. - التاريخ يُعرض بصيغة الـlocale، والزمن النسبي («قبل ٣ د») من طبقة الترجمة. ## 6. مستوى السكافولد — `TripzScaffold` (نقطة التنظيم المركزية) كل شاشة تُبنى داخل `TripzScaffold` — **ممنوع `Scaffold` خام في `features/`**: ```dart TripzScaffold( title: 'رحلاتي', // يمر بالترجمة فعلياً status: state.status, // enum الـCubit كما هو onRetry: cubit.load, // زر إعادة المحاولة في حالة الخطأ empty: EmptyView(...), // حالة اللاشيء bottomAction: TripzButton(...), // خانة CTA السفلية (52px) — keyboard-safe child: ..., // المحتوى عند النجاح فقط ) ``` هو ما يجعل النظام **متوافقاً مع Cubit بالبنية لا بالاتفاق**: الـCubit يُصدر `status` من enum (قانون docs/23 §3)، والسكافولد يحوّله بنفسه إلى skeleton (تحميل) / ErrorView بزر إعادة (خطأ) / EmptyView (فراغ) / المحتوى (نجاح). الشاشة لا تكتب `if (loading)` أبداً — فتتوحّد حالات التطبيق كله من نقطة واحدة، وتغيير شكل «التحميل» لاحقاً سطرٌ واحد. كما يوحّد: SafeArea · حشوة الصفحة (16) · شكل AppBar (مسطّح، بلا ظل، عنوان يسار-حسب-الاتجاه) · سلوك الكيبورد (الـCTA يعلو فوقه). ## 7. عدة المكونات — `core/ui/` الشاشات **تركّب من هذه العدة ولا تستعمل ويدجت Material خاماً بستايل يدوي**: | المكوّن | يغطي | |---|---| | `TripzButton` | primary (معبّأ 52px) · secondary (محدّد) · text — مع حالة تحميل داخلية | | `TripzTextField` | إدخال موحّد: هاتف · OTP · بحث — رسالة الخطأ تحته بالعربية | | `TripzSheet` | كل bottom sheet: مقبض علوي، زوايا 24، ارتفاعات محسوبة — **الحوار الافتراضي في التطبيق sheet لا dialog** | | `TripzCard` | بطاقة الرحلة/السائق/المحفظة | | `StatusBanner` | شريط حالة الرحلة الحي | | `EmptyView` · `ErrorView` · `Skeleton` | الحالات الثلاث — تستهلكها `TripzScaffold` | ملف واحد = مكوّن واحد (قانون docs/23). مكوّن جديد يُضاف هنا أولاً ثم تستعمله الميزة — لا widget مشترك داخل `features/`. ## 8. فلسفة التصميم (المرجع عند كل خلاف ذوقي) 1. **الشاشة مهمة واحدة.** سؤال واحد للمستخدم، زر أساسي واحد في الأسفل. ما زاد نُقل لشاشة أو sheet. 2. **الوضوح قبل الزينة.** لا تدرّجات ولا ظلال ثقيلة ولا زخرفة. الجمال من الفراغ والمحاذاة والخط — النموذج الذهني: تطبيقات آبل الأصلية. 3. **منطقة الإبهام.** كل فعل أساسي في الثلث السفلي. أعلى الشاشة للمعلومة، أسفلها للفعل. 4. **الحالة معروضة دائماً**: skeleton لا spinner فارغ، خطأ بالعربية مع «أعد المحاولة»، فراغ برسالة تدلّ على الفعل التالي. شاشة بيضاء صامتة = باغ. 5. **الحركة وظيفة لا استعراض**: تشرح من-أين-إلى-أين ضمن أزمنة §3، وتُحترم إعدادات تقليل الحركة في النظام. ## 9. قائمة تحقّق الشاشة (تُضاف لقائمة docs/23 §12) - [ ] داخل `TripzScaffold` وبحالات skeleton/error/empty تعمل فعلاً - [ ] لا لون hex ولا رقم مسافة/زاوية خارج tokens — ولا `Scaffold`/Material خام بستايل يدوي - [ ] تعمل بالعربية RTL وبالإنجليزية LTR (تُفحص باللغتين) - [ ] تعمل light وdark (تُفحص بالوضعين) - [ ] النصوص عبر `context.l10n`، الأرقام لاتينية، العملة من country pack - [ ] أهداف اللمس ≥ 48 وتكبير النص حتى 1.3 لا يكسر التخطيط ## 10. البنية في الكود (تحديث شجرة docs/23 §1) ``` lib/core/ ├── design/ │ ├── tokens.dart # المسافات/الزوايا/الحركة — const │ ├── typography.dart # TextTheme حسب الـlocale (Inter + Plex Arabic) │ ├── tripz_colors.dart # ThemeExtension للأدوار الدلالية │ └── theme.dart # buildTheme(brightness, locale) — يستبدل theme.dart الحالي ├── ui/ # عدة المكونات (§7) └── l10n/ # ARB عربي/إنجليزي features/settings/ # SettingsCubit: الثيم + اللغة (محفوظان محلياً) ``` ## 11. ترتيب البناء (يسبق شاشات المجموعة Q) 1. **Q5-أ**: الخطوط (تنزيل الأوزان الثمانية للتطبيقين) · tokens · typography · TripzColors · buildTheme · حذف فرض RTL من `app.dart` · SettingsCubit. 2. **Q5-ب**: `TripzScaffold` + عدة المكونات + ARB — ثم **تُعاد صياغة** شاشات auth/home القائمة عليها كإثبات. 3. بعدها فقط تُبنى شاشات Q1/Q2/Q3 — كل شاشة جديدة تولد ملتزمة من أول سطر.