14 KiB
28 — برومت بناء تطبيقَي فلاتر (يُسلَّم لنموذج البناء كما هو)
طريقة الاستعمال: افتح جلسة جديدة لنموذج البناء وجذر عمله
~/development/App/Tripz، وألصق كل ما تحت الخط. المالك يراجع مخرجات كل مرحلة قبل الإذن بالتي تليها. كُتب 2026-07-19 بإملاء المالك، وكل بند فيه مسنود إلى وثيقة ملزمة فيdocs/.
أنت مكلَّف ببناء تطبيقَي Tripz: الراكب (apps/rider) والسائق (apps/driver) — فلاتر، من الصفر على مستوى Dart، فوق باك إند NestJS مكتمل وجاهز على https://tripz-api.intaleqapp.com/api. المطلوب تطبيق نقل ركاب بمستوى عالمي: سهل، مفهوم، واضح، وفيه إبداع حقيقي في الخريطة وتجربة الطلب — لا قالب جاهز.
1. اقرأ قبل أول سطر كود — بهذا الترتيب، كاملةً
docs/23-flutter-conventions.md— قانون الكود المُلزِم: البنية، Cubit/Bloc، طبقة الشبكة، أعلام الميزات، §15 القناة المزدوجة (جوهر هذا البناء). أي تعارض بينه وبين اجتهادك يُحسم له.docs/26-flutter-design-system.md— قانون الشكل: tokens ·TripzScaffold· عدةcore/ui/· الخطوط (Inter + IBM Plex Sans Arabic مضمّنة محلياً) · ليلي/نهاري · RTL/LTR · ARB.docs/22-full-product-roadmap.md§1.5 والمجموعة Q — الطبقات وأعلامconstوالتسجيل المشروط.docs/27-feature-catalog.md— كتالوج الميزات: ما هو أساسي ثابت وما هو مفتاح مدفوع.docs/03-mobile-plan.md— أين Cubit وأين Bloc كامل (حصراً: دورة رحلة الراكب + عروض السائق).- مستودع سيرو المرجعي
~/development/App/Siro:siro_rider/وsiro_driver/— تدرس السلوك ولا تنسخ الكود (GetX محظور عندنا). عليك خصوصاً فهم:location_controller.dartفي تطبيق السائق: تسجيل كل 3 ثوانٍ · رفع دفعات كل دقيقتين · وعي بالبطارية (تخفيف عند 20٪) · سوكت للبث الحي + دفعات للحفظ. هذا النمط مثبت بالإنتاج — يُنقل سلوكه حرفياً إلى Cubit/Bloc.- كيف يعرض سيرو الرحلة على السائق، وكيف يرسم المسار، وكيف يتصرف عند ضعف الشبكة.
- حزمة الخرائط
intaleq_maps(pub.dev، إصدار 2.2.0، حزمة المالك نفسه فوق MapLibre GL) — واجهتهاLatLng/Marker/Polyline/CameraUpdateبديل drop-in لخرائط جوجل. backend/src/realtime/realtime.gateway.ts— أسماء أحداث السوكت الحقيقية:trip:join·trip:update·trip:offer·trip:offer_taken·trip:new·driver:location·call:offer/answer/ice/end. لا تخترع اسم حدث — اقرأه من هناك.
2. القيود الصلبة — لا تفاوض فيها
- Cubit افتراضاً، Bloc لآلتَي الحالة فقط. لا GetX إطلاقاً. بنية المجلدات حرفياً من docs/23 §1.
intaleq_mapsحصراً. ممنوعflutter_mapوlatlong2وgoogle_maps_flutter(تصادم أنواع موثّق في docs/23 §6).- تقسيم الخرائط: البلاطات من التطبيق إلى انطلق مباشرة؛ geocode / reverse / route / places / autocomplete كلها عبر سيرفرنا (
/maps/*) — مكيّشة Redis وأسرع من النداء المباشر. مفتاح الخريطة من الإعداد، لا في الكود. - الطبقة الأصيلة لا تُمسّ:
android/·ios/·trip_overlay_plugin·bubble-master·secure_string_operations·key/·shorebird.yaml· ملفات Firebase — كلها قائمة ومختبَرة، تُستدعى ولا تُعاد كتابتها. - طبقة الشبكة نقطة واحدة (
core/api/api_client.dart):x-tenant-idمنBuildConfig· Bearer ·x-device-id· توقيع HMAC للنقاط المالية (على الجسم كما يُرسل) · تجديد 401 مرة واحدة. التوكنات فيflutter_secure_storage. - المصادقة هاتف + OTP فقط — لا كلمة مرور، لا Google/Apple.
- أعلام ميزات
const+ تسجيل مشروط في DI والراوتر (docs/23 §5). كل ميزة من كتالوج docs/27 §3 خلف علمها. - لا بناء ولا اختبار على الجهاز — أنت تكتب، والمالك يبني ويشغّل ويعيد إليك النتائج.
- لا منطق تسعير ولا حساب عمولة ولا قرار مطابقة في Dart — السيرفر مصدر الحقيقة، والتطبيق يعرض.
3. القلب المعماري: القناة المزدوجة بسباق داخلي (docs/23 §15 — طبّقه حرفياً)
كل حالة رحلة تصل من قناتين متوازيتين: WebSocket (trip:update) وFCM data-message — والسيرفر يرسلهما معاً على كل انتقال أصلاً. المطلوب في التطبيق:
- مدخل موحّد (
TripEventMerger) يصبّ فيTripBloc(راكب) وOffersBloc(سائق): أيّ القناتين سبقت فحدثها يُطبَّق فوراً. - آلة حالة رتيبة تحسم الازدواج والتعاكس: حدث يحمل حالة أقدم من أو تساوي المعروضة يُهمل بصمت. ترتيب الحالات في
backend/src/modules/trips/trips.service.ts. - صمام المصالحة:
GET /trips/:idعند عودة التطبيق من الخلفية وعند إعادة اتصال السوكت + مؤقّت خفيف أثناء الرحلة النشطة فقط (25 ثانية افتراضاً، قابل للضبط، معطّل خارج الرحلة). - الفتح البارد:
GET /trips/mineلاستئناف رحلة غير منتهية +GET /trips/rating/pending— إن وُجدت رحلة بلا تقييم تُفرض شاشة التقييم قبل أي شيء آخر. - عرض الرحلة على السائق بنفس المبدأ:
trip:offerعبر السوكت + FCM dataOnly بحمولة كاملة (نقاط، مسافة، أجرة، فئة، دفع) → الأسبق يعرض البطاقة/الـoverlay. القبول ذرّي على السيرفر — من خسر السباق يصلهtrip:offer_takenفتختفي البطاقة فوراً مع رسالة لطيفة «الرحلة لم تعد متاحة».
4. التصميم — المرجع البصري DiDi، والفلسفة docs/26 §8
النموذج الذهني: نظافة DiDi وبساطته — خريطة تملأ الشاشة، bottom sheet يقود كل التفاعل، معلومة واحدة كبيرة واضحة في كل لحظة، الأفعال في متناول الإبهام. كل شاشة داخل TripzScaffold، كل مكوّن من core/ui/، كل نص عبر ARB، ليلي/نهاري كاملان (الخريطة تبدّل ستايلها مع الثيم)، RTL/LTR يتبعان اللغة.
الشاشات المفصلية التي يُحكم عليك بها (تُبنى بعناية فائقة):
| الشاشة | المطلوب |
|---|---|
| الهوم (راكب) | خريطة كاملة، دبوس موقعي، sheet سفلي بسؤال واحد: «إلى أين؟» |
| اختيار الوجهة | أوتوكومبليت عبر /maps/places بـdebounce ~300ms + كاش محلي للنتائج الأخيرة + عناوين مفضلة (البيت/العمل) + خيار «اختر من الخريطة» بدبوس يُسحب. دعم نقاط توقف (stops). |
| عرض الفئات والسعر | POST /tariff/quote: بطاقات الفئات (عددها حسب المستأجر — علم) بسعر مقفول لكل فئة، اختيار الدفع (كاش/محفظة)، حقل كوبون، خيار جدولة. |
| البحث عن سائق | حالة searching بحركة نبض على الخريطة + مهلة واضحة + إلغاء سهل. |
| قبول السائق | بطاقة السائق: صورة، اسم، تقييم، المركبة ولونها ولوحتها بارزة، ETA حي، مسار وصول السائق مرسوماً، أزرار اتصال/دردشة. |
| وصول السائق | «وصل سائقك» + عدّاد الدقائق المجانية الخمس ظاهر بصدق (B2) — الراكب يرى ما سيُحاسب عليه قبل حدوثه. |
| أثناء الرحلة | تتبّع حي: ماركر السائق يتحرك بانسياب (tween بين النبضات)، البولي لاين يُعاد رسمه عند انحراف السائق عن المسار (route جديد عبر السيرفر عند تجاوز عتبة انحراف — لا على كل نبضة)، الكاميرا تتبع بذكاء مع زر إعادة توسيط. |
| الإنهاء/الفاتورة | تفصيل الأجرة كما جاء من السيرفر حرفياً: الأجرة، الانتظار الزائد، الخصم، طريقة الدفع — أرقام Inter tabular لا ترقص. |
| التقييم | نجوم + أسباب سريعة اختيارية — وتُفرض عند الفتح إن كانت معلّقة. |
الدراور (القائمة الجانبية) — للطرفين: رحلاتي (سجل بفلاتر) · المحفظة · الكوبونات والدعوات (كود الإحالة) · الإعدادات (لغة/ثيم) · الدعم. يضاف للسائق: الأرباح · الرصيد التشغيلي والشحن (مع حالة الحجب عند تجاوز الأرضية) · وثائقي ومركبتي.
مساحة الإبداع المطلوبة منك (هنا تُظهر مستواك، ضمن قوانين 26): مظهر الخريطة وستايلاها ليلاً ونهاراً، حركة الكاميرا والانتقالات بين مراحل الرحلة، الماركرات المتحركة، إخراج لحظة «تم القبول» و«وصل السائق» بحيث تُحَسّ لا تُقرأ فقط.
5. خصوصيات تطبيق السائق
- متحكّم الموقع بنمط سيرو المذكور في §1.6 — Redis عبر
driver:locationعلى السوكت، ولا يفتح اتصالاً لكل نبضة. - online/offline واضح، ومع الحجب لتجاوز أرضية الرصيد تُعرض رسالة السبب وزر الشحن — لا فشل صامت.
- الـoverlay فوق التطبيقات عبر
trip_overlay_pluginالقائم — يُستدعى من Dart، لا يُعاد بناؤه. - الرحلات المتاحة: قائمة
GET /trips/availableكمكمّل للعرض المباشر. - التسجيل بالوثائق: رفع المستندات والصور (صورتان للمركبة كحد أدنى) وفيديو حيوية الوجه — والسيرفر يتولى Gemini؛ التطبيق يعرض ما استُخرج (
ai_data) مقابل ما أدخله السائق للمطابقة.
6. مراحل التسليم — بوابات مراجعة إلزامية
توقّف في نهاية كل مرحلة وسلّم تقريراً بما بُني (شاشات، ملفات، قرارات) وانتظر مراجعة المالك:
| المرحلة | النطاق | معيار القبول |
|---|---|---|
| 0 | نظام التصميم: docs/26 §11 (خطوط · tokens · typography · TripzColors · buildTheme · TripzScaffold · عدة ui · ARB · SettingsCubit) + إعادة صياغة auth/home القائمتين فوقه | كل قائمة 26 §9 |
| 1 | الراكب: الهوم بالخريطة + اختيار الوجهة والأوتوكومبليت + الفئات والأسعار + إنشاء الطلب حتى searching |
تجربة الطلب كاملة بلا سائق |
| 2 | دورة الرحلة كاملة للراكب + القناة المزدوجة والمصالحة + الفاتورة + التقييم الإجباري | قطع الشبكة/قتل التطبيق في أي مرحلة ثم العودة = الحالة الصحيحة تُستأنف |
| 3 | السائق: العروض (سوكت+FCM+overlay) + التنفيذ + متحكّم الموقع + الرصيد | رحلة كاملة من الطرفين على جهازين |
| 4 | الدراور وملحقاته للطرفين + الدردشة والاتصال (call:* قائم على السوكت) + وثائق السائق |
قائمة docs/23 §12 لكل ميزة |
في كل مرحلة: قائمتا التحقق docs/23 §12 وdocs/26 §9 شرط تسليم، والمالك يبني release ويجرّب — رتّب كودك ليُختبر على أول بناء.
7. ممنوعات صريحة (تُرفض المراجعة عند أيٍّ منها)
GetX · flutter_map/latlong2 · google_fonts الشبكية · نداء HTTP خارج ApiClient · لون hex أو رقم مسافة خارج tokens · نص مرئي خارج ARB · Scaffold خام في features/ · استيراد متقاطع بين الميزات · تعديل الطبقة الأصيلة أو الهويات (applicationId · bundle ID · shorebird) · نسخ كود من سيرو · أي منطق مالي في Dart · الاستماع للسوكت أو FCM من داخل شاشة مباشرة (كل شيء عبر الـBloc الموحّد).
← ذو صلة: 23-flutter-conventions · 26-flutter-design-system · 27-feature-catalog · 22 §Q