تعامل بین کد Dart و پلتفرمهای بومی یک وظیفه کلیدی در توسعه برنامههای Flutter است که نیاز به دسترسی به قابلیتهای دستگاه دارند. به گفته Flutter Team, 2026، Platform Channel همچنان مکانیسم اصلی برای چنین یکپارچهسازیای است و انتقال پیام بین Dart و کد بومی Android و iOS را بدون استفاده از کتابخانههای بومی اضافی فراهم میکند.
نکات کلیدی
Platform Channel فناوری Flutter است که ارتباط دوطرفه بین کد Dart برنامه و کد بومی سیستمهای عامل Android و iOS را فراهم میکند. بدون Platform Channel، برنامه Flutter به قابلیتهای ارائه شده توسط فریمورک محدود میشود و نمیتواند مستقیماً به API دوربین، سنسورها، Bluetooth، سیستم فایل و سایر عملکردهای سطح پایین دستگاه دسترسی داشته باشد.
معماری Platform Channel بر اساس اصل تبادل ناهمزمان پیام ساخته شده است. طرف Dart درخواست را از طریق کانال ارسال میکند، طرف بومی آن را پردازش کرده و نتیجه را برمیگرداند. تمام پیامها به فرمت باینری سریالسازی شده و از طریق بافر پیام Flutter Engine منتقل میشوند که حداقل تأخیر را در انتقال داده بین محیطهای اجرایی تضمین میکند.
هر Platform Channel با یک نام منطقی منحصربهفرد شناسایی میشود — رشتهای که به عنوان آدرس برای مسیریابی پیامها عمل میکند. طرف Dart و طرف بومی باید از همان نام کانال استفاده کنند تا ارتباط به درستی برقرار شود. Flutter از تعداد دلخواه کانال در یک برنامه پشتیبانی میکند و هر کانال مستقل از بقیه کار میکند.
بر اساس مستندات رسمی Flutter، Platform Channel پیامها را به همان ترتیبی که ارسال شدهاند پردازش میکند که قابلیت پیشبینی توالی فراخوانیها را تضمین میکند. این امر در سناریوهایی که ترتیب پردازش بر صحت عملکرد تأثیر میگذارد، مانند مقداردهی اولیه متوالی ماژولهای بومی یا زنجیره عملیات وابسته، حیاتی است.
مکانیسم انتقال پیام از طریق Platform Channel از سه لایه کلیدی تشکیل شده است: طرف Dart پیام را به صورت Map یا List از طریق invokeMethod ارسال میکند، Flutter Engine آن را با استفاده از StandardMethodCodec سریالسازی میکند و طرف بومی فراخوانی را در handler خود دریافت میکند. نتیجه از همان مسیر در جهت مخالف بازگردانده میشود.
فرآیند سریالسازی به طور خودکار انواع داده Dart را به معادلهای آنها در پلتفرمهای بومی تبدیل میکند. اعداد، رشتهها، مقادیر بولی، لیستها و دیکشنریها بدون پیکربندی اضافی از طرف توسعهدهنده پشتیبانی میشوند. انواع داده سفارشی باید قبل از ارسال از طریق کانال به صورت دستی سریالسازی شوند، مثلاً به رشته JSON.
در سمت Flutter Engine، پیام وارد صف رشته اصلی پلتفرم بومی میشود. در Android این رشته اصلی برنامه است، در iOS — حلقه اجرای اصلی. این بدان معناست که عملیات طولانی در handler کانال رابط کاربری را مسدود کرده و باعث هنگ کردن میشود. به توسعهدهندگان توصیه میشود وظایف سنگین را در رشتههای پسزمینه اجرا کرده و نتیجه را به صورت ناهمزمان از طریق callback برگردانند.
عملکرد Platform Channel برای اکثر سناریوهای استفاده به اندازه کافی بالا است: زمان انتقال یک پیام کمتر از 1 میلیثانیه در دستگاههای مدرن است. با این حال، برای عملیات با بار بالا، مانند پردازش جریان ویدئو در زمان واقعی، توصیه میشود از Dart FFI یا پلاگینهای بومی با دسترسی مستقیم به حافظه دستگاه استفاده کنید.
محدودیت کلیدی معماری: Platform Channel از انتقال توصیفگرهای فایل، اشارهگرهای حافظه یا اشیاء بومی پشتیبانی نمیکند. تمام دادهها باید قابل سریالسازی به فرمت باینری باشند. برای انتقال حجم زیادی از داده با اندازه مگابایت، از فایلهای موقت با ارسال مسیر آنها از طریق کانال استفاده کنید.
Flutter سه نوع Platform Channel ارائه میدهد که هر کدام برای سناریوی خاصی از تعامل طراحی شدهاند. انتخاب نوع صحیح کانال معماری یکپارچهسازی و راحتی نگهداری کد را در هر دو طرف — Dart و بومی — تعیین میکند، بنابراین درک تفاوتهای بین MethodChannel، EventChannel و BasicMessageChannel مهم است.
MethodChannel رایجترین نوع Platform Channel است که الگوی فراخوانی رویه از راه دور را پیادهسازی میکند. Dart نام متد و آرگومانها را ارسال میکند، طرف بومی عملیات را اجرا کرده و نتیجه را برمیگرداند. هر فراخوانی Future را برمیگرداند که امکان استفاده از ساختارهای async و await را در کد Dart برای کار ناهمزمان راحت فراهم میکند.
این نوع کانال برای عملیات از نوع درخواست-پاسخ مناسب است: دریافت سطح باتری، خواندن دادههای سنسورها، انجام محاسبات در طرف بومی یا درخواست داده از سرویسهای سیستم. MethodChannel از انواع داده استاندارد از طریق StandardMethodCodec پشتیبانی میکند، از جمله مقادیر null به لطف پشتیبانی Null safety در Dart مدرن.
در پروژههای واقعی، MethodChannel در اکثر پلاگینهای رسمی Flutter استفاده میشود. به عنوان مثال، بستههای camera، battery و path_provider دقیقاً از طریق این نوع کانال کار میکنند و دسترسی به APIهای بومی را بدون نیاز به نوشتن کد یکپارچهسازی خود برای هر پلتفرم فراهم میکنند.
EventChannel برای سناریوهایی طراحی شده است که طرف بومی یک جریان پیوسته از رویدادها را در طول زمان تولید میکند. دادهها از طریق Stream به Dart منتقل میشوند و امکان اشتراک در بهروزرسانیهای زمان واقعی را فراهم میکنند. نمونههای معمول استفاده: خوانش شتابسنج، مختصات GPS، تغییرات وضعیت Bluetooth و اعلانهای سرویسهای سیستم.
برخلاف MethodChannel، EventChannel از مدل انتشار-اشتراک استفاده میکند. طرف بومی رویدادها را در زمان وقوع آنها بدون درخواست صریح از کد Dart ارسال میکند. مشترک در طرف Dart هر رویداد را در یک عنصر جداگانه از استریم دریافت میکند و میتواند دادههای دریافتی را قبل از استفاده در رابط فیلتر یا تبدیل کند.
هنگام استفاده از EventChannel، مدیریت صحیح اشتراکها و لغو آنها ضروری است. هر فراخوانی StreamSubscription باید پس از اتمام کار با کانال لغو شود تا از نشت حافظه در طرف بومی جلوگیری شود. پلتفرم Flutter به طور خودکار استریم را هنگام نابودی ویجت لغو میکند، اما مدیریت صریح اشتراکها قابلیت اطمینان برنامه را در سناریوهای طولانیمدت افزایش میدهد.
BasicMessageChannel انعطافپذیرترین نوع Platform Channel است که برای تبادل ناهمزمان دلخواه پیام طراحی شده است. برخلاف MethodChannel که هر پیام شامل نام متد و آرگومانها است، BasicMessageChannel تنها بار مفید را بدون مسیریابی داخلی منتقل میکند. طرف ارسالکننده پیام را میفرستد، طرف دریافتکننده آن را پردازش کرده و پاسخ را برمیگرداند.
این نوع کانال برای پروتکلهای تعامل سفارشی مناسب است، جایی که ساختار پیامها میتواند بسته به وضعیت برنامه به صورت پویا تغییر کند. BasicMessageChannel به طور پیشفرض از StandardMessageCodec استفاده میکند، اما از جایگزینی MessageCodec دلخواه برای فرمتهای سریالسازی غیراستاندارد پشتیبانی میکند.
در عمل، BasicMessageChannel کمتر از MethodChannel استفاده میشود، زیرا نیاز به پردازش دستی مسیریابی پیامها بدون الگوی نامگذاری داخلی دارد. با این حال، در یکپارچهسازی با کتابخانههای بومی که فرمت پیام خاصی را انتظار دارند، متفاوت از الگوی استاندارد درخواست-پاسخ پیادهسازی شده در MethodChannel، غیرقابل جایگزین است.
بیایید پیادهسازی عملی Platform Channel را با مثال دریافت سطح باتری دستگاه بررسی کنیم. این مثال چرخه کامل کار را نشان میدهد: اعلام MethodChannel در سمت Dart، پیادهسازی handler در Android و iOS، و همچنین مدیریت صحیح خطاها در صورت عدم دسترسی به داده یا عدم وجود مجوزهای لازم.
در سمت Dart، یک نمونه از MethodChannel با نام کانال رشتهای منحصربهفرد ایجاد میشود. متد invokeMethod درخواست را به طرف بومی ارسال میکند و نتیجه را به صورت Future انتظار میکشد. مدیریت خطا از طریق catch PlatformException انجام میشود که طرف بومی در صورت وقوع استثنا در فرآیند پردازش درخواست آن را برمیگرداند.
import 'package:flutter/services.dart';
class BatteryPlugin {
static const _channel = MethodChannel(
'samples.flutter.dev/battery',
);
Future<String> getBatteryLevel() async {
try {
final result = await _channel.invokeMethod<int>(
'getBatteryLevel',
);
return 'Battery level: $result%';
} on PlatformException catch (e) {
return 'Failed: ${e.message}';
}
}
}
در سمت Android، handler در MainActivity از طریق متد configureFlutterEngine ثبت میشود. در داخل setMethodCallHandler، نام متد ورودی بررسی میشود، فراخوانی بومی BatteryManager برای دریافت سطح باتری انجام میشود و نتیجه از طریق شی result بازگردانده میشود. برای متدهایی که توسط کانال پشتیبانی نمیشوند، result.notImplemented فراخوانی میشود.
import android.os.BatteryManager
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)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val level = getBatteryLevel()
if (level != -1) {
result.success(level)
} else {
result.error(
"UNAVAILABLE",
"Battery level not available",
null
)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val manager = getSystemService(BATTERY_SERVICE) as BatteryManager
return manager.getIntProperty(
BatteryManager.BATTERY_PROPERTY_CAPACITY
)
}
}
در پلتفرم iOS، handler در کلاس AppDelegate از طریق FlutterMethodChannel ثبت میشود. کد Swift فراخوانی ورودی را دریافت میکند، به API سیستم UIDevice برای دریافت سطح باتری مراجعه میکند و نتیجه را به Flutter برمیگرداند. پردازش ناهمزمان با weak self امکان اجرای درخواستها را بدون خطر نگهداری چرخه ارجاع قوی در حافظه فراهم میکند.
import UIKit
import Flutter
@UIApplicationMain
class AppDelegate: FlutterAppDelegate {
override func application(
application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger
)
channel.setMethodCallHandler { [weak self] call, result in
if call.method == "getBatteryLevel" {
let level = self?.getBatteryLevel() ?? -1
if level >= 0 {
result(level)
} else {
result(FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil
))
}
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(
application: application,
didFinishLaunchingWithOptions: launchOptions
)
}
private func getBatteryLevel() -> Int {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
return Int(device.batteryLevel * 100)
}
}
Platform Channel در هر موردی که برنامه Flutter به قابلیتهای دستگاهی نیاز دارد که در بستههای استاندارد پیادهسازی نشدهاند، ضروری است. توسعهدهنده باید کانال خود را هنگام یکپارچهسازی با SDKهای بومی دوربین، بیومتریک، NFC، Bluetooth Low Energy یا هنگام کار با سیستم فایل خارج از sandbox برنامه ایجاد کند.
سناریوی اول — استفاده از APIهای بومی که دسترسی مستقیم از Dart به آنها وجود ندارد. این شامل سرویسهای سیستم Android و iOS، سنسورهای سختافزاری با پروتکلهای انتقال داده غیراستاندارد، اعلانهای فشاری با منطق پردازش سفارشی و عملیات رمزنگاری است که نیاز به استفاده از Hardware Security Module برای ذخیره امن کلیدها دارند.
سناریوی دوم — یکپارچهسازی کد بومی موجود در پروژه Flutter. اگر شرکت قبلاً یک کتابخانه بومی برای Android یا iOS توسعه داده است، Platform Channel امکان استفاده مجدد از آن را بدون پورت کردن به Dart فراهم میکند. این کار مهاجرت برنامههای ترکیبی به Flutter را تسریع کرده و سرمایهگذاریها در کد بومی موجود و منطق تجاری انباشته شده را حفظ میکند.
سناریوی سوم — انتشار پلاگین Flutter خود در pub.dev. همه پلاگینهای محبوب از Platform Channel برای ارائه API یکپارچه در Dart استفاده میکنند که در پشت صحنه کد بومی هر پلتفرم را فراخوانی میکند. این رویکرد استانداردی است که توسط تیم Flutter برای ایجاد بستههای قابل استفاده مجدد با پشتیبانی از هر دو پلتفرم موبایل توصیه شده است.
هنگام انتخاب بین ایجاد Platform Channel خود و استفاده از بسته آماده از pub.dev، ابتدا توصیه میشود در دسترس بودن راه حل آماده را بررسی کنید. بستههای camera، geolocator، shared_preferences و path_provider اکثر نیازهای معمول را پوشش میدهند. Platform Channel شخصی فقط در صورت عدم وجود بسته مناسب یا نیاز به سفارشیسازی عمیق رفتار بومی که راه حل موجود ارائه نمیدهد، توجیهپذیر است.
سوالات متداول
MethodChannel الگوی درخواست-پاسخ را با فراخوانی یکباره متد و بازگشت نتیجه از طریق Future پیادهسازی میکند. EventChannel از مدل جریانی استفاده میکند: طرف بومی رویدادها را در زمان وقوع ارسال میکند و Dart آنها را از طریق Stream دریافت میکند. MethodChannel برای عملیات یکباره با انتظار نتیجه مناسب است، EventChannel — برای جریانهای پیوسته داده در زمان واقعی.
Platform Channel از انواع پایه Dart پشتیبانی میکند: int, double, bool, String, List و Map. این انواع به طور خودکار از طریق StandardMethodCodec و StandardMessageCodec به معادلهای بومی سریالسازی میشوند. برای انتقال اشیاء سفارشی، سریالسازی دستی به JSON یا استفاده از MessageCodec دلخواه با پشتیبانی از فرمتهای غیراستاندارد لازم است.
بله، Flutter از تعداد نامحدود Platform Channel در یک برنامه پشتیبانی میکند. هر کانال با یک نام رشتهای منحصربهفرد شناسایی میشود که باید در سمت Dart و پلتفرم بومی یکسان باشد. میتوان کانالهای جداگانه برای ماژولهای مختلف ایجاد کرد: یکی برای دوربین، دیگری برای Bluetooth، سومی برای سنسورها — همه آنها مستقل کار میکنند و بر عملکرد یکدیگر تأثیر نمیگذارند.
در سمت Dart، خطاها از طریق PlatformException مدیریت میشوند که طرف بومی در صورت وقوع استثنا آن را برمیگرداند. بلوک try-catch استثنا را گرفته و به کد، پیام و جزئیات خطا دسترسی فراهم میکند. در سمت بومی، فراخوانی result.error خطا را به Dart برمیگرداند. همچنین متد result.notImplemented برای متدهایی که توسط کانال پشتیبانی نمیشوند در دسترس است.
بله، handler Platform Channel در رشته اصلی پلتفرم بومی اجرا میشود. اگر handler یک عملیات طولانی — درخواست شبکه، خواندن از دیسک یا محاسبات سنگین — انجام دهد، رابط کاربری ممکن است هنگ کند. توصیه میشود وظایف سنگین را در رشته پسزمینه در طرف بومی اجرا کرده و result را فقط پس از اتمام فراخوانی کنید. طرف Dart در این میان به دلیل ماهیت ناهمزمان invokeMethod مسدود نمیشود.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.