المقدمة
فاكر لما اتكلمنا عن Platform Channels وقلنا إنها الجسر اللي بيوصل Flutter بعالم الـ Native؟ 🤔
طب لو انطلب مننا تاسك بيقول: "هات نسبة البطارية دلوقتي" أو "نفّذلي الـ function دي اللي موجودة أصلا في Android"، هنستخدم إيه بالظبط من الـ Platform Channels التلاتة؟
هنا بالظبط بييجي دور MethodChannel 🚀
في المقال ده هنعرف يعني إيه Method Channel، وهي شغّالة إزاي، والرسالة بتاخد إيه من طريق علشان توصل من Dart لـ Android والعكس، وهنطبّق كل ده على مثال عملي كامل.

-
📌 هنتكلم النهاردة عن:
- يعني إيه Method Channel
- إمتى نستخدمها
- مكوناتها الأساسية (الاسم، الـ Method Calls، الـ Codec)
- الـ Constructor الخاص بيها وأنواع البيانات
- مثال عملي من جانب Flutter
- مثال عملي من جانب Android
- رحلة الرسالة بين Flutter و Android
1 - يعني إيه Method Channel؟ 🤔
من أنواع الـ Platform Channels اللي اتكلمنا عنها، عندنا:
- MethodChannel
- EventChannel
- BasicMessageChannel
في المقال ده هنركّز على أهم نوع فيهم، وهو MethodChannel.
الـ MethodChannel هي قناة بيستخدمها Flutter علشان يتواصل مع الكود الـ Native في Android أو iOS، عن طريق انه بيستدعي methods بشكل Asynchronous.
بيستخدم لما يكون عندك method معينة بتتنفذ وبترجّع نتيجة بعدها على طول، زي ما اقول للـ Android مثلًا: "نفّذلي function اسمها getBatteryLevel، جيبلي نسبة البطارية دلوقتي، ورجّعلي النتيجة في الأبلكيشن.
💡خد بالك : مش بس Flutter هو اللي بينادي على الـ Native، لأ Android أو iOS كمان يقدروا ينادوا على كود Dart. التواصل بيمشي في الاتجاهين اكنك بتكلم واحد في التليفون و بيرد عليك من الناحية التانية.

2 - إمتى نستخدم Method Channel؟
بنستخدمها لما يكون عندنا عملية أو method معينة محتاجين ننفذها على الـ Native، ونرجّع منها نتيجة. زي مثلًا:
- قراءة مستوى البطارية
- الوصول لإمكانيات معينة في الجهاز
- استخدام API موجود في Android أو iOS ومش متاح مباشرة في Flutter
- التعامل مع كود Native موجود عندنا بالفعل

الفكرة الأساسية عندنا بتتلخص ف ان :

3 - Method channel بتتكوّن من إيه؟
فيه كام مفهوم أساسي لازم نفهمهم قبل ما نبدأ نكتب الكود.
1️⃣ اسم القناة (Channel Name)
علشان تكلم حد في التليفون، لازم تكون عارف رقمه. وهنا برضو، القناة لازم يكون ليها اسم معين. أهم حاجة في الـ channel هي اسمها:

ممكن نعتبر الاسم ده هو عنوان القناة. يعني Flutter وAndroid لازم يكونوا متفقين انهم بيتكلموا على نفس العنوان.
⚠️خلي بالك لو عندك كذا قناة بنفس الاسم، ده بيعمل مشاكل في الاتصال بين Flutter وAndroid. لازم كل قناة يكون ليها اسم مستقل.
2️⃣ Method Calls
الـ Method Channel مش مجرد قناة بتبعت messages عادية، هي مبنية على فكرة method calls:

هنا Flutter بيقول للـ Native: "نفّذ method اسمها getBatteryLevel". وممكن كمان نبعت arguments معاها:


3️⃣ التحويل لـ Binary (Encoding / Decoding)
قبل ما الـ method call تنتقل بين Flutter والـ Native، البيانات بتتحوّل إلى Binary. يعني لما نكتب:

Flutter بيحوّلها لـ ( Binary 0 , 1) ويبعتها للـ Android.
ولما النتيجة ترجع، بتيجي Binary تاني وFlutter يحولها مرة كمان:
يعني في الآخر Flutter يقدر يستقبل النتيجة كـ int أو String أو List أو Map.

طب مين مسؤول عن الـ Encoding والـ Decoding؟
هنا بيظهر مفهوم اسمه Method Codec، انا لو بتكلم عربي وواحد تاني بيتكلم انجليزي مش هنفهم بعض لازم نتفق علي لغة واحدة نتواصل بيها هنا برضو علشان فلاتر و اندرويد يفهموا بعض لازم يكون في حاجة تحول من دا ل دا ف method codec هو المسؤول عن تحويل method calls والـ results ل Binary والعكس. يعني لازم Flutter وAndroid يستخدموا نفس لغة التحويل.
الـ default في Method Channel هو:

وده بيستخدم Standard Message Codec جواه علشان يشفر القيم المسموح بيها، زي: int, double, String, bool, List, Map, وبيانات bytes. والـ serialization بيحصل تلقائي، إحنا مش محتاجين نحوّل كل حاجة لـ Binary بإيدينا.
📌 نقطة مهمة: لو بعتنا حاجة مش مدعومة (زي object معقّد جدًا)، هيطلعلي exception.
4️⃣ ازاي الرسائل بتترتب ؟
الـ MethodChannel بترتب الرسايل بطريقة FIFO ordering. يعني الرسائل بتمشي بنفس الترتيب اللي اتبعتت بيه:
call1 → call2 → call3 (كده هتوصل)
call2 → call1 (ده مش هيحصل)
4 - عندنا بعد كدا الـ Constructor بتاع Method Channel
ده شكل الـ constructor:

خلونا نشوفه واحدة واحدة شرح الكود دا :
1️⃣ name — ده اسم القناة، زي
MethodChannel('samples.flutter.dev/battery')،
ولازم الـ Native يستخدم نفس الاسم بالظبط.
2️⃣ codec — ده المسؤول عن طريقة تحويل الـ method calls والنتائج. غالبًا ده الـ default (StandardMethodCodec()) ومش بنحتاج نغيّره.
3️⃣ binaryMessenger — ده المسؤول عن إرسال واستقبال الـ bytes الخاصة بالقناة. في الحالة العادية Flutter بيستخدم الـ defaultBinaryMessenger.
⚠️ لو طلع معاك error اسمه MissingPluginException، ده غالبًا بسبب إن الـ isolate ماكانش عنده BinaryMessenger. وهنعرف علاقة الـ MethodChannel بالـ isolate في الجزء الجاي.
5 - أنواع البيانات (Data Types)
Flutter بيستخدم StandardMessageCodec علشان يسمح بتبادل بيانات زي:
- أرقام (int / double)
- نصوص (String)
- true/false
- Lists
- Maps
- byte buffers
💡 الجميل إن التحويل (serialization) بيحصل أوتوماتيك، ومش محتاج تعمل parsing بإيدك.

5 - دلوقت هنطبق مثال عملي: قراءة نسبة البطارية 🔋
لو عايزين نعرف نسبة البطارية في الموبايل ازاي هنربط بين فلاتر و اندرويد ؟
اول حاجة Flutter يقدر يتواصل مع الكود الاصلي لكل نظام عن طريق channel واحدة:

وكل بلات فورم ياخد الفانكشن و بينفّذ الكود المناسب ليه:
- Android → يستخدم BatteryManager
- iOS → يستخدم device.batteryLevel
- Windows → يستخدم GetSystemPowerStatus
- Linux → يستخدم UPower
يعني تخيّل معايا دلوقت :
- MethodChannel = خط اتصال
- 'samples.flutter.dev/battery' = اسم الخط
- platform = السماعة اللي في إيدك
طيب هنعمل ايه ؟ كل اللي عايزينه اننا نجيب نسبة البطارية من الموبايل، ونعرضها في Flutter UI.
ال Flow هيكون بالشكل دا :
Flutter يطلب المعلومة → الـ Platform ينفّذ → النتيجة ترجع لـ Flutter → Flutter يعرضها في الـ UI بس كدا.
ازاي ننفذ دا بقي ؟
1️⃣ اول حاجة هنعمل الـ Channel

يعني ايه السطر دا:
- MethodChannel → معناها "أنا بعمل قناة تواصل مع الكود الأصلي (Android / iOS)". دي الوسيلة اللي Flutter بيبعت بيها أوامر للـ platform ويستقبل منها ردود.
- 'samples.flutter.dev/battery' → اسم القناة، زي رقم تليفون. لازم يكون مطابق 100% في Flutter وفي الكود الـ Native. وعادةً بنستخدم prefix زي domain علشان نقلل احتمالية تعارض أسماء الـ Channels.
- const → القيمة ثابتة، مش هتتغيّر، وبالتالي الـ Channel نفسها مش محتاجة تتعمل كل مرة.
- static → المتغيّر تبع الكلاس نفسه، مش لكل Object منه، فنقدر نستخدمه علطول من غير إنشاء Instance جديدة.
- platform → مجرد اسم المتغيّر اللي شايل الـ MethodChannel، وعشان كده هنستخدم platform.invokeMethod(...) بعد كده.
2️⃣ متغيّر الحالة (State)

ده اللي هيخزّن نسبة البطارية الحالية ويتعرض في الـ UI. في البداية بنحط Unknown battery level. لأننا لسه ما طلبناش النسبة من الـ Platform.
3️⃣ بعد كدا عندنا الـ Function اللي بتكلّم الـ Platform

أهم سطر فيها:

دي معناها: "كلم الـ platform، قوله نفّذ getBatteryLevel، واستنى الرقم اللي هيرجع وخزّنه في result".
- platform → القناة اللي عرّفناها قبل كده.
- invokeMethod<int>() →
استدعاء method من الـ platform، والـ <int> معناها إننا متوقّعين النتيجة تكون من نوع int. كأني بقول: "أنا مستني رقم يرجعلي، اللي هو مثلًا 80 في المية".
- 'getBatteryLevel' → اسم الـ Method اللي Flutter بيطلب تنفيذها، ولازم يكون متطابق بين الطرفين.
- await → لأن invokeMethod عملية Asynchronous، الطلب بيروح للـ Platform وبعدها النتيجة بترجع. فـ await معناها "استنى النتيجة قبل ما تكمل"، ومن غيرها الكود ممكن يكمل قبل ما النتيجة توصل.
- final result → المتغيّر اللي هيخزّن النتيجة، لو البطارية 80% يبقى result = 80.
تخيّل إنك بتبعت واتساب — invokeMethod هو إنك تبعت الرسالة ، await هو إنك تستنى الرد ، وresult هو الرد اللي رجعلك .
🔴 ازاي نتعامل مع الاخطاء ؟
بنستخدم السطر دا :

ليه بنحتاجها؟ ممكن مثلًا:
- الـ API مش متاحة على الجهاز
- الـ Platform ما نفّذش الـ Method
- حصل خطأ أثناء تنفيذ الطلب
- أو بيئة التشغيل زي Emulator ما بتوفّرش المعلومة بالشكل المتوقع
عشان كده بنستخدم PlatformException بدل ما التطبيق ينهار.
و بعد ما رجعلنا النتيجة هنعرضها في UI

💡 setState() بتقول لـ Flutter: "البيانات اتغيّرت، اعمل Rebuild للجزء اللي بيعتمد عليها".
4️⃣ اخر حاجة هنعرض النسبة بقي ف ال UI

Text(_batteryLevel)
بيعرض "Unknown..." في الأول، وبعد كده نسبة البطارية زي Battery level at 85 %.
الكود كامل على بعضه (Flutter side)
كده جمّعنا كل حتة لوحدها، دا شكل الكود كله مع بعضه في مكان

واحد:
7 - نيجي بقي الطرف التاني من القصة: Android بيرد على Flutter
Flutter قال
invokeMethod('getBatteryLevel'). هنا Android لازم: يستقبل الطلب، ينفّذ الكود، ويرجّع النتيجة.
الكود ده بيتكتب فين اصلا في بروجكت فلاتر؟
كود الـ Native بيتكتب في ملفات كود Android جوه كلاس
MainActivity
الكود مش بيتحط في أي مكان وخلاص جوه الـ Activity؛ إحنا بنعمل Override لدالة اسمها
configureFlutterEngine(flutterEngine: FlutterEngine).ليه الدالة دي بالذات؟ لأنها اللحظة اللي محرك فلاتر (FlutterEngine) بيتم تهيئته فيها وبيكون جاهز، فبنقدر نوصل للـ binaryMessenger ونبني الـ MethodChannel ونربط الـ setMethodCallHandler لاستقبال الطلبات وتنفيذ ميثود getBatteryLevel.
(وعلشان كدا configureFlutterEngine أول خطوة لتجهيز الاتصال في جانب Android).
في Flutter Android project، تقدر تكتبه باستخدام:
Kotlin or Java اي حاجة الفكرة واحدة
والاختيار بينهم راجع للـ Android project أو الـ plugin اللي هتشتغل عليه.
علشان نكتب الكود بقي :
1️⃣ اول حاجة عندنا هي تعريف اسم الـ Channel في Android

💡 مهم جدًا:زي ما قلنا نفس الاسم اللي في Flutter لازم يكون مطابق 100%، وده اللي بيربط الطرفين ببعض.
2️⃣ إنشاء الـ MethodChannel في Android

معناها إننا بنقول: "افتح قناة استقبال للرسائل الجاية من Flutter".
- flutterEngine.getDartExecutor() → بنوصل من خلالها للـ Dart Engine.
- getBinaryMessenger() → الوسيلة اللي بتسمح بتبادل الرسائل بين Flutter والـ Native.
- CHANNEL → اسم القناة، زي samples.flutter.dev/battery
.
3️⃣ استقبال الرسائل

عندنا حاجتين مهمين هنا:
- call → الطلب اللي وصل من Flutter، ومن خلاله نعرف اسم الـ Method اللي Flutter طلبها.
- result → اللي هنستخدمه علشان نرجّع الرد إلى Flutter.
4️⃣ نعرف Flutter طالب إيه

هنا بنسأل: "هل Flutter طلب getBatteryLevel؟"
5️⃣ ننفّذ الكود الحقيقي (Android API)

دي function Native بتجيب نسبة البطارية من Android، ومن أمثلة الـ APIs المستخدمة: BatteryManager في الأجهزة الحديثة. الفكرة الأساسية إن هنا بيبدأ الكود الأصلي Native، وFlutter نفسه مش هو اللي بيقرأ البطارية مباشرة.
6️⃣ نرجّع النتيجة

result.notImplemented() معناها: "أنا مش فاهم الطلب ده، أو الـ method دي مش موجودة عندي".
يبقي كدا عندنا Execution Flow :
الطلب من Flutter بيكون بالشكل دا

في Android يستقبل :
- getBatteryLevel
- يشغّل getBatteryLevel()
- يجيب النسبة
- يرجّعها بـ result.success
- يرجع لـ Flutter كـ result = 85.
أهم 3 نقط تاخد بالك منهم:
- اسم القناة لازم يكون نفس اللي في Flutter
- setMethodCallHandler هو اللي بيستقبل الطلبات
- result.success هو اللي بيرجّع النتيجة
كده عرفنا ازاي التواصل بيتم بين فلاتر و اندرويد .
7- خلونا نشوف كواليس الرحلة على بعضها بتمشي ازاي :

1️⃣ الرسالة بتطلع من Flutter

Flutter هنا زي ما قلنا بيحوّل الرسالة دي لـ Binary Message باستخدام StandardMessageCodec، وبعد كده بيبعتها للـ platform.
تخيّلها كأنك كاتب رسالة وحاططها في ظرف.
2️⃣ الرسالة بتوصل للـ Android

getBinaryMessenger() هنا هو زي "ساعي البريد" ، وMethodCallHandler هو اللي بيستقبل الرسالة. Android بيحلل الـ binary message ويرجع يعرف إيه المطلوب (getBatteryLevel).
💡 هنا كأن الساعي وصّل الرسالة للموظف الصح
3️⃣بعد كدا Android ينفّذ الكود ويرجّع النتيجة

result.success(...) بيرجّع الرد لـ Flutter، والرد يتحوّل تاني لـ Binary Message عن طريق الـ channel.
💡و هنا الموظف كتب الرد وحطّه في ظرف وردّه للساعي
4️⃣ و اخيرا Flutter تستقبل النتيجة

Flutter يستقبل الـ binary message،
يحوّله إلى (int) بعد عمل Decoding
ويحط القيمة في _batteryLevel، والشاشة تتحدّث أوتوماتيكيًا.
💡 و هنا خلاص انا فتحت الظرف وشفت الرسالة 📬
يعني كدا Flutter بتعمل عندها :
- MethodChannel(...) → إنشاء القناة
- invokeMethod(...) → إرسال الطلب
- setState() → تحديث الشاشة
و من ناحية Android بنعمل :
- configureFlutterEngine() → تجهيز الاتصال
- MethodChannel(...) → استقبال الطلب
- setMethodCallHandler → التعامل مع الطلب
- result.success() → إرسال الرد
أهم نقطة في الرحلة كله هي القناة (samples.flutter.dev/battery) هي اللي رابطة كل ده نفس الاسم = الاتصال شغال، اسم مختلف = مفيش تواصل
الخاتمة
وبكده نبقى خلّصنا أول جزء في الـ MethodChannel: عرفنا يعني إيه، هي شغّالة إزاي، والرسالة بتاخد إيه من طريق علشان توصل من Dart لـ Android والعكس.
المرة الجاية هنكمل الحكاية ونتكلم عن علاقة MethodChannel بالـ Threads بشكل أعمق، وكمان التطور الجديد للـ MethodChannel وباكدج Pigeon 🔮
تقدر تشوف ملخص ليها من هنا
لو بتحب تسمع شوف فيديو بسيط من هنا :
💌 Happy Coding & Smooth Apps

Discussion