Flutter ولا Native: تختار إيه لتطبيقك؟
يوسف محمدآخر تحديث 15 دقيقة قراءة
الإجابة باختصار
في أغلب تطبيقات البيزنس، زي العرض والحجز والتوصيل والمحتوى والتسوّق وبوابات العملاء، Flutter هو الاختيار العملي: بتكتب التطبيق مرة واحدة بلغة Dart، وبينزل على App Store و Google Play تطبيق حقيقي متترجم لكود الجهاز.
أما التطوير الـ Native، يعني Swift لأجهزة iPhone و Kotlin لأجهزة Android، فهو الأنسب لما المنتج يكون قايم على أحدث مزايا منصّة معيّنة، أو على الواقع المعزّز والرسوميات الـ 3D التقيلة، أو على تكامل عميق مع نظام التشغيل، أو لما تكون محتاج منصّة واحدة بس.
عشان كده أغلب مشاريع تطبيقات الموبايل اللي بشتغل عليها بتبقى بـ Flutter. تطبيق 90Plus، وده تطبيق كورة فيه نتايج لايف وأسئلة يومية وتوقّعات، نازل على المتجرين بالعربي والإنجليزي. وتطبيق Online Ezzay تطبيق توصيل معمول للعربي الأول، بتعمل منه الطلبات وتتابع الشحنات لحظة بلحظة. والاتنين من النوع اللي Flutter مناسب له: شاشات كتير، وبيانات لحظية، وواجهة عربي.
Flutter بيرسم الشاشة إزاي؟
تطبيق iOS الـ Native بيتكتب بلغة Swift، والأيام دي غالبًا بـ SwiftUI، وبيستخدم زراير Apple وقوايمها وخانات الإدخال بتاعتها. وتطبيق Android الـ Native بيتكتب بلغة Kotlin، والأيام دي غالبًا بـ Jetpack Compose، وبيستخدم مكوّنات Google. يعني إنت قدام تطبيقين ولغتين وفريقين، أو مطوّر واحد شاطر في الاتنين.
Flutter بيشتغل بطريقة مختلفة. كود Dart بيتترجم مسبقًا لكود الجهاز في نسخ الإصدار، وبعدين Flutter بيرسم كل بكسل بنفسه بمحرّك رسم خاص بيه (Impeller هو الافتراضي على iOS و Android). يعني هو مش بيغلّف زراير النظام، لكن بييجي بعناصره هو بشكلين: Material، وشكل Cupertino اللي بيقلّد iOS.
عشان كده شاشة Flutter بتبان واحدة على كل الأجهزة، ونفس كود التصميم بيشتغل على المنصّتين. وفي المقابل، Flutter نفسه لازم يلحق شكل كل منصّة وطريقة تصرّفها، وده غالبًا بيعمله كويس. وقارئات الشاشة شغالة كمان، لأن Flutter بيبعت وصف لكل شاشة لـ VoiceOver و TalkBack.
الأداء في تطبيقات البيزنس الحقيقية
في تطبيق البيزنس، المستخدم بياخد باله من تلات حاجات: التطبيق بيفتح بسرعة قد إيه، والـ scroll ناعم ولا لأ، وبيستنى البيانات قد إيه. تطبيق Flutter المتبني كويس بيفتح بسرعة والـ scroll فيه ناعم على الموبايلات المتوسطة. أما انتظار البيانات فده على السيرفر والشبكة، مش على الـ framework.
ولما تطبيق Flutter يقطّع، السبب عادةً بيبقى في كود التطبيق نفسه: صور ضخمة بتتفك بحجمها الكامل، أو شاشة كاملة بتتبني من الأول مع كل تغيير صغير، أو شغل تقيل زي تحليل response كبير على الـ main thread. وتطبيقات الـ Native بتقطّع لنفس الأسباب بالظبط.
وحل المشكلة الأخيرة إنك تنقل الشغل لـ Isolate في الخلفية، وده في Dart كام سطر بس.
ودايمًا احكم على السرعة من نسخة release أو profile على موبايل متوسط حقيقي، لأن نسخة debug أبطأ عن قصد ومتنفعش تحكم بيها على أي تطبيق.
import 'dart:convert';
import 'dart:isolate';
Future<List<Order>> parseOrders(String body) {
// Decoding a large response on the main isolate blocks the UI.
// Isolate.run moves the work to a background isolate.
return Isolate.run(() {
final items = jsonDecode(body) as List<dynamic>;
return [
for (final item in items) Order.fromJson(item as Map<String, dynamic>),
];
});
}الكود الواحد بيوفّر إيه فعلًا؟
مع Flutter فيه مجموعة شاشات واحدة، وقواعد شغل واحدة، ومجموعة اختبارات واحدة. الميزة بتوصل للمتجرين في نفس اليوم، والغلطة بتتصلّح مرة واحدة مش مرتين.
وبيوفّر كمان في التنسيق. مع تطبيقين Native، كل تغيير بيتصمّم ويتبني ويتجرّب وينزل مرتين، والتطبيقين بيبعدوا عن بعض واحدة واحدة: زرار في مكان على iPhone وفي مكان تاني على Android، أو ميزة وصلت لواحد فيهم والتاني لأ.
بس هو مش بيقسم المشروع كله على اتنين. التصميم، والـ Backend، والمحتوى، والاختبار على الأجهزة، والشغل على المتاجر، كلهم بياخدوا نفس الوقت في الحالتين. Flutter بيختصر الجزء اللي بتتكتب فيه الشاشات والمنطق، وده غالبًا الجزء الأكبر، بس مش الجزء الوحيد.
اللي لسه مطلوب لكل منصّة لوحدها
فيه شغل بيفضل خاص بكل منصّة، مهما كانت طريقة البناء:
- حسابين مطوّر: Apple Developer Program و Google Play Console
- التوقيع: الشهادات وملفات الـ Provisioning لـ iOS، ومفتاح الرفع و Play App Signing لـ Android
- الصلاحيات: النصوص اللي بتوضّح الصلاحية بتتستخدم في إيه في Info.plist لـ iOS، وتعريف الصلاحيات في AndroidManifest.xml لـ Android
- الإشعارات: خدمة الإشعارات بتاعة Apple محتاجة مفتاح وإعداد خاص بيها، حتى لو بتستخدم Firebase Cloud Messaging للمنصّتين
- فورمات الخصوصية: بيانات App Privacy في App Store Connect، وفورم Data safety في Google Play
- صفحة التطبيق والـ screenshots والمراجعة في كل متجر
- جهاز Mac عليه Xcode، أو خدمة build على الـ cloud فيها أجهزة Mac، عشان تبني نسخة iOS
- الاختبار على أجهزة iPhone و Android حقيقية بمقاسات مختلفة
مزايا الجهاز والـ Packages
Flutter بيوصل لمزايا الجهاز عن طريق packages منشورة على pub.dev. والمزايا اللي بتتستخدم كل يوم متغطّية كويس: الكاميرا واختيار الصور، والخرايط، واللوكيشن، والإشعارات، والشرا من جوه التطبيق، وتسجيل الدخول بالبصمة أو الوش، والـ deep links، والتخزين على الجهاز، وعرض صفحات الويب. وكتير منها فريق Flutter نفسه أو فريق Firebase هو اللي بيصونه.
قبل ما تلتزم، اكتب المزايا اللي التطبيق مينفعش ينزل من غيرها، ودوّر على package لكل واحدة. وفي كل package بص على الناشر (علامة الناشر الموثّق بتفيد)، وآخر تحديث كان إمتى، والمنصّات اللي بتدعمها، وبيتعاملوا إزاي مع المشاكل المفتوحة.
ولو بوابة الدفع أو مزوّد الخرايط أو مورّد جهاز معيّن مش بيقدّم غير SDK Native، فده مش معناه إن Flutter خرج من الحسابات. بس ده شغل زيادة لازم يدخل في الخطة والتسعير من الأول.
إزاي تستدعي كود Native من تطبيق Flutter
لما ميبقاش فيه package جاهزة، زي SDK لبوابة دفع محلية أو مكتبة scanner من مورّد أجهزة، الجزء ده بيتكتب بكود Native ويتنادى من Dart عن طريق Platform Channel. كود Kotlin و Swift بيسجّل handler بنفس اسم القناة، و Dart بيناديه زي أي async function.
وفي التكاملات الأكبر، فيه package اسمها Pigeon من فريق Flutter بتولّد كود محدّد الأنواع للطرفين، فمتبقاش بتبعت بيانات ملهاش شكل محدد. وفي الحالتين الجزء الـ Native عادةً بيفضل صغير، وباقي التطبيق بيفضل مكتوب بـ Flutter.
وهنا الحد الحقيقي لـ Flutter: لازم يكون في المشروع حد مرتاح في التعامل مع Xcode و Android Studio. لأن مطوّر Flutter اللي مش بيقرّب من الكود الـ Native هيقف عند أول SDK مش معتاد.
import 'package:flutter/services.dart';
class NativeScanner {
// The Swift and Kotlin code registers a handler under this same name.
static const _channel = MethodChannel('com.example.app/scanner');
/// Opens the vendor's native scanner and returns the scanned code.
Future<String?> scan() async {
try {
return await _channel.invokeMethod<String>('scan');
} on PlatformException {
return null; // The native side reported an error.
} on MissingPluginException {
return null; // No native handler on this platform.
}
}
}حجم التطبيق
تطبيق Flutter بيشيل معاه المحرّك بتاعه والـ framework، عشان كده تطبيق Flutter الفاضي بيبقى أكبر بكام ميجابايت من تطبيق Native فاضي. لكن في التطبيق الحقيقي، أتقل حاجة عادةً هي الصور والخطوط والأنيميشن و packages الطرف التالت، ودول وزنهم واحد مهما كانت طريقة البناء.
والمتجرين أصلًا بيبعتوا لكل موبايل اللي محتاجه بس: Google Play بيبني من الـ App Bundle نسخة مناسبة لكل جهاز، و App Store بيصغّر التحميل لكل جهاز. و Flutter كمان يقدر يوريك بالظبط إيه اللي واخد المساحة، ويطلّع الـ debug symbols بره التطبيق.
# See what takes space in an Android release build
flutter build appbundle --analyze-size --target-platform android-arm64
# Release build with Dart symbols stored outside the app
# (keep build/symbols to read crash reports later)
flutter build appbundle --obfuscate --split-debug-info=build/symbolsتجربة مناسبة لـ iPhone و Android
عشان Flutter بيرسم عناصره بنفسه، فالتطبيق يقرّب من طابع كل منصّة قد إيه ده قرارك إنت. فيه حاجات بتتظبط لوحدها: الانتقال بين الصفحات، والسحب للرجوع على iPhone، والـ scroll اللي بيرتد على iOS وبيتمط على Android، ومقابض تحديد النص.
وحاجات تانية محتاجة قرار: الـ switches، والـ dialogs، والـ date picker، ومؤشرات التحميل. Flutter بيوفّر نسخ adaptive زي Switch.adaptive و showAdaptiveDialog بتاخد شكل iOS على أجهزة Apple. وبراندات كتير بتختار تصميم خاص واحد للمنصّتين، وده مقبول طالما التنقّل والإيماءات بيتصرّفوا زي ما مستخدم كل منصّة متوقّع.
اللي ببعد عنه هو تطبيق باين إنه Android على iPhone: تأثيرات Material في كل حتة، وزرار عايم في الركن، و date picker و dialogs بشكل Android. المستخدم ممكن ميعرفش اسم المشكلة، بس بيحسّ بيها.
دعم العربي والاتجاه من اليمين للشمال
دي من نقط قوة Flutter في التطبيقات اللي رايحة لمصر والخليج. فعّل اللغة العربي والتصميم كله بيتقلب من اليمين للشمال، بشرط إن الهوامش والمحاذاة تتكتب بـ start و end مش بـ left و right. والتواريخ والأرقام وصيغ الجمع بتيجي من package اسمها intl، والترجمات بتتحفظ في ملفات ARB و Flutter بيحوّلها لكود Dart.
والمنصّات الـ Native بتدعم العربي كويس برضه: SwiftUI بيستخدم leading و trailing، و Compose بيستخدم start و end. الفرق إنك مع الـ Native بتعمل شغل الاتجاه والترجمة والاختبار مرتين، في مشروعين، ولازم تخلّيهم متطابقين.
وفي الحالتين، جرّب بنص عربي حقيقي مش بنص إنجليزي مؤقّت، وراجع المحتوى المخلوط زي أرقام التليفونات والأسعار وأسماء البراندات الإنجليزي جوه الجمل العربي. وكتبت دليل لوحده عن العربي والاتجاه من اليمين للشمال في Flutter بيشرح التفاصيل.
التوظيف والصيانة والمخاطر على المدى الطويل
مع Flutter بتوظّف لمهارة واحدة. ومع الـ Native محتاج Swift و Kotlin، وده غالبًا معناه مطوّرين اتنين، أو حد نادر شاطر في الاتنين. وفي الشركات الصغيرة الفرق ده بيفرق أكتر من أي مقارنة أداء.
والصيانة مش صفر في أي طريق فيهم. Apple و Google بيطلّعوا نسخة جديدة من نظام التشغيل كل سنة، و Google Play بيعلّي مستوى الـ target API المطلوب من التطبيقات على طول، و Apple بتعلّي أقل إصدار Xcode بتقبله وقت رفع التطبيق. وتطبيق Flutter محتاج كمان تحديث لـ Flutter والـ packages بتاعته كذا مرة في السنة. فخطّط لصيانة دورية حتى في سنة مفيهاش مزايا جديدة.
أما المخاطر على المدى الطويل، فـ Flutter مفتوح المصدر، و Google بتطوّره مع مجتمع كبير من براها، والكود اللي بتملكه مكتوب بلغة Dart عادية. والخطر الحقيقي مش إن Flutter يختفي، لكن إن package مهمة من طرف تالت صيانتها تقف. عشان كده قلّل الـ dependencies، وفضّل الرسمي منها، وحط كل package ورا Class صغير بتاعك عشان يبقى سهل تبدّلها.
والـ Native ليه نسخته من الخطر ده: SwiftUI و Compose بيتغيّروا بسرعة، وكود UIKit أو Android Views القديم محتاج شغل عشان يفضل حديث.
الويب والديسكتوب: ميزة إضافية
Flutter يقدر كمان يبني للويب و Windows و macOS و Linux من نفس الكود. وده مفيد للوحة تحكّم، أو لوحة متابعة للموظفين، أو أداة داخلية شبه التطبيق.
بس مش مناسب لموقعك العام. Flutter على الويب بيرسم جوه Canvas، وبيحمّل كود أكتر في أول زيارة، وضعيف في الظهور في جوجل. عشان كده موقع الشركة أو المدوّنة أو المتجر الأونلاين لازم يبقى موقع عادي متبني لمحركات البحث. اعتبر الويب والديسكتوب في Flutter ميزة إضافية، مش سبب تختاره عشانه.
و React Native موقعه فين؟
React Native هو الاختيار الأساسي التاني لبناء تطبيق واحد للمنصّتين. بتكتب فيه JavaScript أو TypeScript مع React، وبيعرض المكوّنات الـ Native بتاعة كل منصّة. وفريق React Native بينصح إن المشاريع الجديدة تبدأ بـ framework زي Expo، اللي بيتولّى الـ build وكتير من الـ native modules.
بيبقى منطقي لما فريقك شغال أصلًا بـ React على الويب وعايز يشارك الناس وطريقة الشغل وشوية من الكود مع التطبيق. و Flutter بيبقى منطقي لما تكون عايز واجهة واحدة متّسقة متحكّم فيها بالكامل، وأداة واحدة لكل اللي بيظهر على الشاشة. الاتنين ناضجين، واللي بيحسم عادةً هو الفريق اللي عندك، مش الـ framework.
أنا ببني بـ Flutter، فخد رأيي وإنت عامل حساب ده. ولو عندك فريق React قوي، فـ React Native اختيار معقول جدًا.
إمتى الـ Native يبقى الاختيار الأوضح؟
بنصح بالـ Native، أو بجزء Native على الأقل، في الحالات دي:
- الواقع المعزّز أو الرسوميات الـ 3D اللحظية هي أساس المنتج، باستخدام ARKit أو RealityKit أو ARCore
- المنتج لعبة: محرّك ألعاب زي Unity أو Godot عادةً بيبقى أنسب، مع إن محرّك Flame في Flutter بيكفي للألعاب البسيطة الـ 2D
- التكامل العميق مع النظام هو المنتج نفسه: CarPlay أو Android Auto، أو كيبوردات أو VPN، أو معالجة تقيلة في الخلفية، أو شغل دقيق مع أجهزة Bluetooth
- ودجات الشاشة الرئيسية أو Live Activities أو تطبيق Apple Watch هي التجربة الأساسية مش حاجة زيادة عليها. ودول بيتكتبوا بكود Native حتى جوه تطبيق Flutter
- محتاج ميزة جديدة في نظام التشغيل من يوم ما Apple أو Google يطلقوها
- المنتج لمنصّة واحدة، زي تطبيق لـ iPad بس أو تطبيق Android لأجهزة الشركة، فالكود المشترك مش هيوفّر حاجة
- عندك تطبيق Native شغال وفريق Native، وإعادة كتابته نادرًا ما بتستاهل التعب
غلطات منتشرة وإنت بتختار
دي غلطات بحاول أساعد العملاء يبعدوا عنها:
- إنك تختار على أساس فيديوهات مقارنة الأداء. اختار على أساس قائمة المزايا وفريقك.
- إنك تتوقع إن Flutter هيقسم الميزانية كلها على اتنين. هو بيوفّر في الشاشات والمنطق، مش في التصميم والـ Backend والاختبار والشغل على المتاجر.
- إنك تفترض إن التطبيق الـ Native أسرع أوتوماتيك. التطبيق الـ Native المتبني وحش بيقطّع هو كمان، وجودة البناء أهم من الـ framework.
- إنك تأجّل أخطر ميزة. لو التطبيق معتمد على SDK واحد أو ميزة معيّنة في الجهاز، ابنيلها نموذج صغير في أول أسبوع.
- إنك تنسى الـ Backend. الـ API ولوحة التحكّم والإشعارات كتير بيبقوا بحجم التطبيق نفسه.
- إنك توظّف مطوّر Flutter عمره ما بيفتح Xcode أو Android Studio، فالإصدارات والتوقيع والـ SDKs الـ Native تقف.
- إنك تعيد كتابة تطبيق شغال لمجرد إن فيه framework جديد بقى منتشر.
قائمة القرار والخطوة الجاية
- محتاج iOS و Android مع بعض وقت الإطلاق، ولا ممكن واحدة فيهم تستنى؟
- في المزايا الأساسية فيه واقع معزّز أو رسوميات 3D أو تطبيق ساعة أو ودجت أو تكامل عميق مع النظام؟
- كل SDK إنت معتمد عليه، زي بوابة الدفع أو الخرايط أو الشات، بيدعم Flutter أو ينفع يتربط عن طريق Platform Channel؟
- التطبيق هيبقى بالعربي ولا بالإنجليزي ولا باللغتين؟
- مين اللي هيمسك الصيانة، وإيه المهارات اللي عنده؟
- فيه تطبيق أو كود موجود عايز تحافظ عليه؟
- محتاج كمان لوحة تحكّم على الويب؟
لو محتاج المنصّتين، والمزايا مزايا بيزنس، ومالقيتش في أسئلة الواقع المعزّز والـ SDKs عائق ملوش حل، فـ Flutter هو الاختيار الافتراضي المعقول. ولو منصّة واحدة كفاية، أو المنتج في منطقة محدش بيتقنها غير الـ Native، فاختار الـ Native.
الخطوة الجاية: اكتب صفحة واحدة فيها مين المستخدمين، وإيه المزايا الأساسية، وأنهي منصّات، وأنهي لغات، وإيه الأنظمة اللي التطبيق هيتربط بيها. وعلّم على ميزة أو اتنين مش متأكد منهم. وبعدين ابعت الصفحة ليّا أو لأي شركة برمجة بتفكّر فيها، واسأل سؤالين: بينصحوا بأنهي طريقة وليه، وهيبنوا نموذج لإيه الأول عشان يشيلوا المخاطرة. الإجابة الكويسة بتذكر مزايا وعيوب كل اختيار، والضعيفة مش بتذكر غير الأداة اللي هما بيستخدموها على طول.
ولو عايز رأيي في مشروعك، كلّمني من الموقع ده، وهقولك هبنيه بأنهي طريقة وإيه اللي بيحدد التكلفة في حالتك.
أسئلة شائعة
- هل Flutter سريع زي تطبيقات الـ Native؟
- في تطبيقات البيزنس زي التسوّق والحجز والتوصيل والمحتوى، المستخدم مش هيحس بفرق في نسخة release متبنية كويس. Flutter بيتترجم لكود الجهاز وبيرسم بالمحرّك بتاعه. وتفضل تطبيقات الـ Native ليها الأفضلية في الرسوميات الـ 3D التقيلة والواقع المعزّز والشغل القريب جدًا من نظام التشغيل.
- برمجة التطبيق بـ Flutter أرخص من الـ Native؟
- غالبًا أيوه لما تكون محتاج iOS و Android مع بعض، لأن الشاشات والمنطق بيتكتبوا مرة واحدة وفريق واحد بيصونهم. بس هو مش بيقلّل التصميم أو الـ Backend أو الاختبار أو الشغل على المتاجر. والتكلفة الحقيقية بتعتمد على المزايا والتكاملات والتصميم، فاطلب عرض سعر مبني على قائمة مزايا مكتوبة.
- تطبيق Flutter بيبان زي تطبيقات iPhone على iOS؟
- ينفع. التنقّل والـ scroll وتحديد النص بيمشوا على طريقة iOS أوتوماتيك، والعناصر الـ adaptive بتاخد شكل iOS. وبراندات كتير بتختار تصميم خاص واحد للمنصّتين، وده بينجح برضه طالما الإيماءات بتتصرّف زي ما مستخدم كل منصّة متوقّع.
- تطبيق Flutter يقدر يستخدم مزايا iOS و Android الـ Native؟
- أيوه. أغلب مزايا الجهاز ليها packages في Flutter، وأي حاجة تانية بتتكتب بـ Swift أو Kotlin وتتنادى عن طريق Platform Channel. أما ودجات الشاشة الرئيسية وتطبيقات الساعة فبتتكتب بكود Native وبتتبادل البيانات مع تطبيق Flutter.
- أنهي أحسن: Flutter ولا React Native؟
- الاتنين اختيارات ناضجة لبناء تطبيق واحد للمنصّتين. اختار React Native لو فريقك شغال أصلًا بـ React و TypeScript، واختار Flutter لو عايز أداة واجهات واحدة متحكّم فيها بالكامل وشكل واحد على كل الأجهزة. والفريق اللي عندك أهم من الـ framework.
- ممكن Google توقّف دعم Flutter؟
- محدش يقدر يضمن أي شركة هتعمل إيه في المستقبل. بس Flutter مفتوح المصدر، و Google بتطوّره بنشاط وبتستخدمه في شوية من تطبيقاتها، وفيه مجتمع كبير بيساهم فيه. وكودك مكتوب بـ Dart وملكك، والخطر الأكبر في الشغل اليومي إن package من طرف تالت صيانتها تقف، وده بتتعامل معاه بإنك تقلّل الـ dependencies.
- ينفع أنقل تطبيق Native موجود لـ Flutter واحدة واحدة؟
- أيوه. ميزة add-to-app في Flutter بتخليك تضيف شاشات Flutter لتطبيق iOS أو Android موجود، ميزة ورا ميزة. لكن ده منطقي بس لما يكون فيه سبب واضح، زي إنك توحّد تطبيقين Native بعدوا عن بعض. أما التطبيق الـ Native اللي شغال كويس والفريق بتاعه مرتاح، فالأحسن غالبًا تسيبه زي ما هو.