درج في قاعة مظلمة تحت صفوف من الأضواء
تطوير Flutter

تطبيقات Flutter من شيفرة واحدة.

تطبيقات متعددة المنصات بإحساس أصلي على iOS وAndroid. شيفرة واحدة، ومدة أقصر، وفريق يضع Flutter في الإنتاج فعلًا.

بالتفصيل

ما يجب معرفته قبل البدء.

01أين تتفوق

ما تجيده Flutter فعلًا.

لا تغلّف Flutter مكوّنات واجهة المنصة. بل ترسم كل بكسل بنفسها عبر محرّك العرض الخاص بها، وDart يُترجَم مسبقًا إلى شيفرة آلة أصلية. وهذا القرار المعماري الواحد يفسّر معظم نقاط قوة Flutter وكل نقاط ضعفها، لذا يستحق أن يُفهم قبل اختيارها.

  • اتساق التصميم: تبدو الشاشة كما هي على المنصتين وعلى جهاز Android قديم، لأن إطار العمل هو الذي رسمها بدل أن يطلب ذلك من نظام التشغيل.
  • الواجهات المخصّصة: الحركة والانتقالات والتصميم ذو الحضور العلامي عمل عادي في Flutter بدل أن يكون صراعًا مع عناصر تحكم المنصة.
  • فريق واحد ولغة واحدة ومجموعة اختبارات واحدة لـ iOS وAndroid، مع hot reload يبقي حلقة التصميم قصيرة بما يكفي للتكرار مع مصمّم في الغرفة.
  • أداء يمكن التنبؤ به لنوع التطبيقات التي تبنيها معظم الشركات: قوائم ونماذج ووسائط ورسوم بيانية ونداءات API.
  • تتجاوز الهواتف — فهناك أهداف للويب وسطح المكتب والأنظمة المضمّنة — وهذا يهم منتجًا قد يحتاج لاحقًا إلى كشك أو أداة سطح مكتب داخلية.

وهي خيار ناضج لا مقامرة. فـ Google تبني منتجاتها بها، وتطبيقات استهلاكية كبيرة تصدر عليها، ووتيرة الإصدارات ثابتة بما يكفي للتخطيط حولها.

02أين لا تتفوق

أين تكون Flutter الأداة الخاطئة.

  • حين يعتمد المنتج على SDK من مورّد لا يوجد إلا لـ Swift وKotlin وتكون إضافة المجتمع مهجورة. يمكنك بناء جسر، لكنك صرت تصون جسرًا.
  • حين يكون التطبيق في معظمه واجهات نظام: عناصر الشاشة الرئيسية، والأنشطة الحية، وتطبيقات الساعة، وامتدادات المشاركة. فهذه تُكتب أصليًا مهما كان التطبيق الرئيسي مبنيًا به.
  • حين يكون المطلوب أن يبدو مطابقًا للمنصة، في كل عنصر تحكم، وإلى الأبد. فمكوّنات Cupertino في Flutter تحاكي iOS؛ وهي لا تتغيّر يوم تغيّره Apple.
  • حين يكون فريقك على هيئة React وسيصون التطبيق بنفسه. فأفضل إطار عمل هو الذي يستطيع أفرادك إبقاءه حيًا.
  • حين يكون حجم التثبيت قيدًا صارمًا في سوق أجهزته منخفضة الإمكانات وبياناته باهظة. فـ Flutter تشحن محرّكًا قبل أن تشحن شاشتك الأولى.

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

03الاقتصاد

شيفرة واحدة، بصراحة.

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

لكنه لا يجعل المنصة الثانية مجانية، ومن يقول لك غير ذلك لم يُصدر واحدة. وما يبقى خاصًا بكل منصة:

  • الأذونات والتنفيذ في الخلفية وسلوك الإشعارات تختلف، ولكل منها إعداده واختباره.
  • الحضور في المتاجر مكرَّر: حسابان، ومراجعتان، ومجموعتا مواد، وجدولا إصدار.
  • الإعداد الأصلي يعيش في مشروعَي iOS وAndroid — التوقيع، والاستحقاقات، وملفات manifest، وملفات ارتباط الروابط العميقة.
  • بعض الإضافات تتصرّف على نحو مختلف بين المنصتين، وقليل من العلل يظهر في واحدة منهما فقط.
  • الاختبار يجري على الاثنتين. الشيفرة المشتركة ليست جهازًا مشتركًا.

والخلاصة الصادقة: الشيفرة الواحدة تجعل منصتين أرخص فعليًا من بناءين أصليين، وأغلى فعليًا من منصة واحدة. نقدّر على هذا الأساس، ويتغيّر التقدير حين يتغيّر النطاق — كتابةً، قبل بدء العمل، وليس في صمت.

04الترحيل

نقل تطبيق قائم إلى Flutter.

إعادة الكتابة أغلى طريقة لإصلاح تطبيق، لذا فالسؤال الأول هل التطبيق الحالي هو المشكلة فعلًا. إن كانت شيفرتان أصليتان قد تباعدتا، وصارت كل ميزة تُبنى مرتين، ويذهب نصف الميزانية إلى إبقائهما متسقتين، فشيفرة Flutter واحدة جواب حقيقي. أما إن كان التطبيق يبدو بطيئًا لأن الخلفية بطيئة، فتغيير إطار عمل الواجهة لا يغيّر شيئًا البتة.

طريقتان لفعلها

تدريجيًا: تُضاف Flutter إلى التطبيق القائم وتتولى منطقة بعد أخرى، فيستمر المستخدمون في تلقّي الإصدارات وتبقى المخاطرة محصورة. وهي أغلى في التمديدات — نظاما بناء، وتنقّل عبر الحدود، ونظام تصميم مشترك — وهي عادةً الخيار الصحيح لتطبيق له مستخدمون حقيقيون وإيراد حقيقي.

إعادة بناء كاملة: أرخص لكل ميزة وأبسط في التفكير، وهي القرار الصحيح حين يكون التطبيق القائم صغيرًا أو غير موثَّق أو مبنيًا على شيء لم يعد أحد يصونه. والفخ هو النطاق. فإعادة البناء تتحوّل بهدوء إلى إعادة تصميم، ثم تستغرق ضعف التقدير الذي اتفق عليه الجميع.

ما ننظر إليه قبل التوصية بأيّهما

  • كم من التطبيق خاص بالمنصة فعلًا، لا مجرد شاشات ونماذج ونداءات API.
  • هل الخلفية والـ API مستقران بما يكفي للبناء عليهما، أم أنهما جزء من المشكلة.
  • أي الاعتماديات الأصلية موجودة اليوم، وهل لكل منها بديل مصان في Flutter.
  • هل للتطبيق الحالي أي اختبارات. فبدونها يصير تكافؤ الميزات مسألة رأي.
  • من سيصون النتيجة، وبأي لغة.

يعود الجواب مكتوبًا، مع تعليله، ومعه حجة عدم فعل شيء. فإعادة الكتابة التي لا يستطيع أحد تبريرها على الورق هي إعادة الكتابة التي يُندَم عليها.

05الشيفرة الأصلية

قنوات المنصة والعمل الأصلي.

تستطيع Flutter بلوغ كل ما تستطيعه المنصة. والسؤال الوحيد من يكتب الجسر. قنوات الطرق (method channels) تمرّر رسائل موصوفة الأنواع بين Dart وSwift أو Kotlin. وPigeon يولّد ذلك الكود التكراري من مخطط، فيزيل صنفًا كاملًا من عدم التطابق وقت التشغيل بين الطرفين. أما dart:ffi فينادي مكتبات C وC++ مباشرة بلا قناة أصلًا، وهو عادةً الطريق الصحيح إلى SDK أصلي مكتوب بلغة C.

كيف نتعامل مع الإضافات

معظم الاحتياجات تغطيها حزمة جاهزة، واختيار الحزمة قرار صيانة لا نتيجة بحث. وقبل أن تدخل أي اعتمادية ننظر متى صدر آخر إصدار منها، وهل تدعم إصدار Flutter الحالي والمنصتين كما ينبغي، وكيف تُعالَج المشكلات المفتوحة، وكم سيصعب استبدالها. فالحزمة الشائعة المهجورة أسوأ من حزمة صغيرة مصانة.

وحيث لا يوجد شيء مناسب نكتب الإضافة — بصيغة اتحادية (federated) حين يختلف السلوك فعلًا بين المنصتين — وتعيش في مستودعك، مختبَرة على الاثنتين، فلا يضطر أحد إلى تفكيكها بعد سنة.

06الحجم والسرعة

حجم الحزمة، والأشياء التي تسبّب التقطيع فعلًا.

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

  • أصدر نسخًا لكل معمارية. فحزمة Android App Bundle تقدّم لكل جهاز الشيفرة التي يحتاجها فقط.
  • خطوط الأيقونات والأصول غير المستخدَمة تُقلَّم في نسخ الإصدار؛ وما ينفخ الحزمة فعلًا هو الصور الكبيرة والوسائط المضمّنة.
  • التحميل المؤجَّل يمكن أن يُخرج الأجزاء نادرة الاستخدام من تطبيق Android من التنزيل الأولي.
  • راجِع الاعتماديات من حيث الوزن، لا من حيث الميزات فقط.

الأداء عمليًا

التقطيع في تطبيق Flutter نادرًا ما يكون سببه إطار العمل. بل يكون في الغالب عملًا يجري على خيط الواجهة وموضعه مكان آخر: فك ترميز الصور بدقتها الكاملة، أو إعادة بناء شجرة كاملة لأن الحالة تقع في مكان أعلى مما ينبغي، أو تحليل حمولة JSON كبيرة تزامنيًا بدل تحليلها في isolate، أو بناء كل عناصر القائمة بدل المرئي منها فقط. ونحن نحلّل الأداء في وضع الإصدار على هاتف Android متوسط الفئة لا على أحدث iPhone، لأن هناك تقع المشكلة.

07الويب وسطح المكتب

الويب وسطح المكتب هدفان، لا هدية.

تُترجَم Flutter إلى الويب وإلى سطح المكتب، وكلاهما يعمل. ولا شيء منهما مجاني، واختيارهما لسبب خاطئ هو ما يوقع المشروع في مأزق.

Flutter على الويب

الصفحة لوحة رسم يرسم عليها إطار العمل. وهذا يجعلها طريقة معقولة لإصدار لوحة معلومات داخلية أو أداة إدارية خلف تسجيل دخول مع إعادة استعمال شيفرة التطبيق نفسها. وهي خيار رديء لأي شيء يجب أن يُعثَر عليه في البحث أو يقرأه زاحف: التنزيل الأولي ثقيل، وما في الـ HTML قليل، والنص لا يتصرّف كما يتصرّف في مستند. فإن كان على الصفحة أن تحتل ترتيبًا، فابنِها بـ Next.js ودع Flutter تملك التطبيق.

سطح المكتب

نسخ Windows وmacOS وLinux حقيقية ومفيدة للأدوات الداخلية. ولكل منها تغليفها وتوقيعها وتوثيقها (notarisation) وآلية تحديثها — وهذا مشروع منفصل بميزانيته، لا خانة تُعلَّم في إعداد البناء.

08الاختبار

الاختبار والبنية والبقاء على المستجدات.

  • اختبارات وحدة لمنطق الأعمال، حيث تكلّف العلل أكثر ما يكون وتكلّف الاختبارات أقل ما يكون.
  • اختبارات widget للشاشات وحالاتها — التحميل والفراغ والخطأ والممتلئة — لأن هذه هي الحالات التي تُشحن معطوبة.
  • اختبارات تكامل للمسارات التي يجب ألا تنكسر أبدًا: تسجيل الدخول، والدفع، والإجراء الأساسي في المنتج.
  • اختبارات golden حيث يكون التراجع البصري مهمًا فعلًا.
  • كل ذلك في CI مع كل طلب دمج، مع توزيع النسخ على المختبِرين آليًا.

والبنية تخدم الهدف نفسه. منطق الأعمال يقع بعيدًا عن الـ widgets، وإدارة الحالة خيار واحد يُطبَّق باتساق — Riverpod أو BLoC، يُقرَّر مرة ويُكتب بدل أن يُخلَط بين شاشة وأخرى — والوصول إلى المنصة يقع خلف واجهات ليمكن تزييفه في الاختبار. وFlutter نفسها تتحرك بسرعة، لذلك تُجدوَل ترقية الـ SDK واعتمادياته كعمل يُنجَز على خطوات صغيرة، بدل قفزة عبر عدة إصدارات حين يفرض شيء ما ذلك أخيرًا.

أسئلة

الأسئلة التي تسبق العقد.

لماذا Flutter بدل تطبيقين أصليين؟
كود Dart واحد يُترجَم إلى iOS وAndroid، فتُصمَّم الشاشة وتُبنى وتُختبر مرة واحدة. والواجهة يرسمها Flutter نفسه، فيظهر التطبيق متطابقًا على المنصّتين وعلى الأجهزة الأقدم. ولتطبيقات المنتجات — شاشات وبيانات ونماذج ووسائط وإشعارات — يكون الوفر في العمل حقيقيًا.
متى تنصحوننا بعدم استخدام Flutter؟
عندما يكون التطبيق مجرّد غلاف رقيق فوق قدرات خاصة بالمنصّة: تكامل عميق مع النظام، أو معالجة ثقيلة في الخلفية، أو ودجات شاشة رئيسية وإضافات نظام، أو حزمة تطوير من مُصنّع لا تتوفّر إلا أصليًا. وكذلك إذا كانت منصّة واحدة تكفيك. نقول ذلك أثناء تحديد النطاق، لا بعد التوقيع.
هل يستطيع Flutter استخدام الخادم الموجود لدينا؟
نعم. Flutter عميل يتعامل مع REST أو GraphQL مثل أي عميل آخر. نقرأ أولًا عقد واجهتك البرمجية ونحدّد ما ينقصها للهاتف، وغالبًا يكون التقسيم إلى صفحات، ورموز الإشعارات، ونقاط تدعم العمل دون اتصال، وترقيم الإصدارات.
هل سيبدو تطبيقًا حقيقيًا، وما حجمه؟
يُترجَم Flutter إلى كود ARM أصلي، وتعمل القوائم والحركات والتنقّل بسلاسة كاملة عند البناء الصحيح. وحجم الملف يبدأ أكبر من تطبيق أصلي بسيط لأن محرّك الرسم يُشحن معه، والفارق بضعة ميغابايت نقلّصه ببناء نسخة لكل معمارية وتنظيف الموارد.
هل يعطينا الكود نفسه نسخة ويب أو سطح مكتب؟
تقنيًا نعم، لكننا نحدّد نطاقها بشكل منفصل ولا نبيعها كأنها مجانية. الويب وسطح المكتب يحتاجان تنقّلًا وتعاملًا مع الإدخال وتخطيطًا واختبارًا خاصًّا بكل منهما. وإذا كان المطلوب على الويب محتوى يجب أن تجده محرّكات البحث، فنوصي عادةً بتطبيق ويب حقيقي بدلًا من ذلك.
كيف يُسعَّر المشروع، ولمن تعود ملكيته؟
سعر ثابت مقابل نطاق مكتوب، أو أجر شهري للعمل المستمر، ويُحدَّد بعد جلسة تحديد نطاق لا تكلّف شيئًا. والكود والتصميم وملفات المتاجر ملك لك، في مستودع تتحكّم فيه منذ أول commit.
اتصل بنا

لديك مشروع في ذهنك؟

أخبرنا بما تبنيه. ستحصل على قراءة صادقة للنطاق والجدول الزمني والتكلفة.