Method Channel هي آلية اتصال ثنائي الاتجاه بين كود Dart والجانب الأصلي لنظامي iOS وAndroid في Flutter. وفقاً لـ Flutter Documentation, 2026، يوفر Method Channel نقل الرسائل المُنمطة بين Dart ومنصة المضيف. بدون هذه الآلية، من المستحيل الوصول إلى إمكانيات الأجهزة للجهاز، وSDK الأصلية، واستدعاءات النظام من كود التطبيق.
أهم النقاط
Method Channel هو المكون المركزي لطبقة المنصة في Flutter، حيث تتبادل عزلات Dart الرسائل مع التطبيق المضيف على iOS أو Android. المهمة الرئيسية للقناة هي إخفاء الاختلافات في بروتوكولات نقل البيانات بين المنصتين وتوفير واجهة برمجة تطبيقات موحدة للمطور.
عندما يحتاج تطبيق Flutter إلى الوصول إلى الكاميرا أو Bluetooth أو المستشعرات أو أي API أصلي آخر، فإن الاستدعاء المباشر من Dart غير ممكن. يعمل Flutter في محرك مبني على C++ ولا يمكنه الوصول إلى أطر UIKit أو Android SDK. تحل Method Channel هذه المشكلة عن طريق إنشاء جسر بين عالم Dart وعالم الكود الأصلي.
وفقاً لـ Google I/O 2024، أكثر من 80% من تطبيقات Flutter في الإنتاج تستخدم قناة Method Channel واحدة على الأقل للتكامل مع خدمات المنصة. وهذا يؤكد الدور الحاسم للقناة في هندسة المشاريع الحديثة.
بالنسبة للمطور، تبدو Method Channel كاستدعاء دالة غير متزامنة عادية. تحت الغطاء، يحدث تسلسل للرسالة، ونقلها عبر مخزن المحرك المؤقت، وتنفيذ الكود الأصلي على الخيط الرئيسي للمنصة.
يبدأ التفاعل عبر Method Channel عندما يرسل جانب Dart رسالة تحتوي على اسم الطريقة والوسائط. يتلقى Flutter Engine هذه الرسالة، ويحولها إلى التنسيق القياسي StandardMethodCodec، ويمررها إلى الجانب الأصلي عبر BinaryMessenger.
يحتوي الجانب الأصلي على معالج — MethodCallHandler، الذي يتلقى الاستدعاء المُفكك التسلسل وينفذ المنطق المقابل. يتم إرجاع النتيجة مرة أخرى إلى Dart كاستجابة Response تحتوي إما على نتيجة ناجحة أو خطأ مع رمز ورسالة.
يمكن تقسيم دورة الاستدعاء بأكملها عبر Method Channel إلى ست مراحل. تنشئ عزلة Dart مثيلاً للقناة باسم فريد لتحديد الاتصال. عند استدعاء invokeMethod، يقوم كود منصة Dart بتسلسل اسم الطريقة والوسائط باستخدام MethodCodec، الذي يحولها إلى مخزن مؤقت ثنائي عبر StandardMessageCodec.
ينقل Flutter Engine هذا المخزن المؤقت عبر مقبس إلى الجانب الأصلي. يقرأ BinaryMessenger الأصلي الرسالة، ويحدد القناة بالاسم، ويستدعي المعالج المسجل، ويمرر إليه كائن FlutterMethodCall مع البيانات المُحللة. ينفذ المعالج الكود المطلوب ويعيد النتيجة، التي تمر عبر مسار التسلسل العكسي وتصل إلى Dart كـ Future.
تتكون هندسة Method Channel من عدة كيانات مترابطة، كل منها مسؤول عن مرحلته الخاصة في نقل البيانات. توفر واجهة برمجة تطبيقات Dart الفئة MethodChannel، التي تخفي عن المطور التفاصيل منخفضة المستوى للتسلسل والتوجيه.
BinaryMessenger هي واجهة منخفضة المستوى لمحرك Flutter لإرسال واستقبال الرسائل الثنائية بين Dart ومنصة المضيف. ترتبط كل MethodChannel بـ BinaryMessenger محدد يوفر التوجيه حسب اسم القناة. في جانب Dart تُستخدم فئة BinaryMessenger، وفي Android — BinaryMessenger من حزمة io.flutter.embedding.engine، وفي iOS — بروتوكول FlutterBinaryMessenger.
MethodCodec هو مشفر يحول استدعاءات الطرق والقيم المُعادة إلى تنسيق ثنائي. يأتي Flutter مع تطبيقين مدمجين: StandardMethodCodec (افتراضي) و JSONMethodCodec (للسلاسل JSON). يستخدم StandardMethodCodec داخلياً StandardMessageCodec، الذي يسلسل البيانات مع دعم جميع الأنواع الأساسية لـ Dart.
StandardMessageCodec يدعم مجموعة محدودة من أنواع البيانات لضمان التوافق بين Dart و Kotlin و Swift. تشمل القائمة: null و bool و int و double و String و Uint8List و Int32List و Int64List و Float64List و List و Map بمفاتيح نصية.
جميع الأنواع الأخرى — DateTime وكائنات DTO أو الفئات المخصصة — يجب تحويلها إلى أحد التنسيقات المذكورة. النهج الأكثر شيوعاً هو تسلسل الكائنات المعقدة إلى Map مع حقول وإعادة بناء الهيكل في الجانب المستقبل من قاموس الحقول.
لنقل البيانات الثنائية الكبيرة، مثل الصور من الكاميرا، يوصي Flutter باستخدام BasicMessageChannel مع Uint8List لتجنب النسخ الكامل للمخزن المؤقت في كل استدعاء عبر MethodChannel.
| نوع Dart | نوع Kotlin | نوع Swift |
|---|---|---|
| null | null | nil |
| bool | Boolean | NSNumber |
| int | Int | NSNumber |
| double | Double | NSNumber |
| String | String | NSString |
| Uint8List | ByteArray | FlutterStandardTypedData |
| List | List | Array |
| Map | HashMap | Dictionary |
يتم إعداد Method Channel في جانب Android في فئة تنفذ FlutterPlugin، أو مباشرة في MainActivity. النهج الأول هو الموصى به لأنه يوفر إدارة مناسبة لدورة حياة الإضافة والتوافق مع سيناريوهات add-to-app.
بعد إنشاء مثيل للقناة بنفس الاسم الموجود في جانب Dart، من الضروري تسجيل MethodCallHandler عبر setMethodCallHandler. داخل المعالج، يتحقق المطور من اسم الطريقة الواردة باستخدام when ويعيد النتيجة عبر result.success أو خطأ عبر result.error مع رمز ورسالة.
package com.example.app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = getBatteryLevel()
if (batteryLevel != null) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery not available", null)
}
}
else -> result.notImplemented()
}
}
}
}
في هذا المثال، تتعامل القناة المسماة samples.flutter.dev/battery مع استدعاء getBatteryLevel، وتحصل على مستوى الشحن عبر Android BatteryManager وتعيده إلى كود Dart. يجب أن يتطابق اسم القناة على كلا الجانبين، وإلا لن تصل الرسالة إلى المعالج.
بالنسبة لكود الإنتاج، يوصى بعزل منطق Method Channel في فئة منفصلة تنفذ FlutterPlugin. يتيح ذلك إعادة استخدام الإضافة بين المشاريع ويضمن تنظيف الموارد بشكل صحيح عند استدعاء onDetachedFromEngine. يتم تسجيل الإضافة عبر registerWith ويمكن اختبارها بشكل منفصل عن Activity.
يتم إعداد Method Channel على iOS في فئة تنفذ بروتوكول FlutterPlugin، أو في AppDelegate. النهج الموصى به هو إنشاء فئة إضافة منفصلة يتم تسجيلها عبر FlutterPluginRegistrar وتديرها Flutter Engine.
يرسل جانب Dart استدعاءً، ويتلقى المعالج الأصلي كائن FlutterMethodCall باسم الطريقة والوسائط. يحدد المطور الطريقة المستدعاة عبر switch على call.method ويعيد النتيجة عبر إغلاق result. للوصول إلى واجهات برمجة تطبيقات iOS، يتم استخدام UIKit وأطر النظام الأخرى.
import Flutter
import UIKit
public class BatteryPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: registrar.messenger())
let instance = BatteryPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getBatteryLevel":
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
let level = Int(device.batteryLevel * 100)
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
}
يضمن نهج FlutterPlugin التسجيل الصحيح وإلغاء تنشيط الإضافة عند تدمير Flutter Engine. في معالج Swift، يُستخدم switch على call.method، كل حالة تعيد نتيجة عبر إغلاق result. الوسائط متاحة عبر call.arguments مع التحويل إلى النوع المناسب.
عند العمل مع Method Channel، من المهم اتباع عدة قواعد رئيسية لضمان أداء واستقرار التطبيق. التوصية الرئيسية هي تقليل كمية وحجم البيانات المنقولة، خاصة أثناء الاستدعاءات في حلقات الرسوم المتحركة أو بتردد عالٍ.
في الجانب الأصلي، يجب دائماً معالجة الاستثناءات وإرجاع خطأ عبر result.error مع رسالة مقروءة. في جانب Dart، يجب أن يكون كل استدعاء invokeMethod مغلفاً في try-catch لالتقاط PlatformException. تجاهل الأخطاء قد يؤدي إلى تعطل التطبيق بشكل غير متوقع دون سبب واضح.
افتراضياً، ينفذ Method Channel الكود الأصلي على الخيط الرئيسي للمنصة. إذا كان المعالج ينفذ عملية ثقيلة، فيجب نقل التنفيذ إلى خيط خلفية باستخدام Kotlin Coroutines على Android أو Grand Central Dispatch على iOS. يجب إرجاع النتيجة عبر result فقط بعد اكتمال العمل على الخيط الرئيسي.
اختر أسماء فريدة للقنوات باستخدام ترميز المجال العكسي — على سبيل المثال، com.example.app/feature. الأسماء القصيرة قد تتعارض مع الإضافات الأخرى. يسجل Flutter القنوات بشكل عام، لذا تتسبب الأسماء المتطابقة في إضافات مختلفة في استبدال المعالج واستدعاءات معطلة.
الأسئلة الشائعة
MethodChannel مخصص لاستدعاء الطرق بنمط طلب-استجابة مع الترميز عبر MethodCodec. يرسل BasicMessageChannel رسائل عشوائية بدون تنسيق طريقة ووسائط، وهو مناسب لـ البيانات المتدفقة والأحداث من المنصة.
مباشرة — لا. يدعم StandardMessageCodec الأنواع الأساسية فقط: الأنواع البدائية و String و Uint8List و List و Map. يجب تسلسل الكائنات المخصصة يدوياً إلى Map قبل الإرسال وإعادة بنائها في الجانب المستقبل من قاموس الحقول.
في الجانب الأصلي، استخدم result.error مع رمز خطأ ورسالة. في جانب Dart، لف invokeMethod في try-catch والتقط PlatformException. إذا لم يتم تنفيذ الطريقة على المنصة، أعد result.notImplemented.
كل استدعاء يقوم بتسلسل ونسخ البيانات بين العزلات والمنصات. للاستدعاءات النادرة، الحمل الإضافي ضئيل. عند نقل ميغابايتات من البيانات لكل إطار، قد تحدث تأخيرات وانخفاض في FPS. للبيانات المتدفقة، استخدم طرق عرض المنصة أو كائنات العرض المحكم.
استخدم EventChannel — فهو مصمم لتدفق الأحداث من الجانب الأصلي إلى Dart. تبدأ المنصة الإرسال عبر EventSink، ويشترك Dart في التدفق باستخدام receiveBroadcastStream. Method Channel غير مناسب لهذا السيناريو.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.