การโต้ตอบระหว่างโค้ด Dart และแพลตฟอร์มเนทีฟเป็นงานสำคัญเมื่อพัฒนาแอปพลิเคชัน Flutter ที่ต้องการเข้าถึงความสามารถของอุปกรณ์ ตามข้อมูลจาก Flutter Team, 2026 Platform Channel ยังคงเป็นกลไกหลักสำหรับการรวมดังกล่าว ช่วยให้สามารถส่งข้อความระหว่าง Dart และโค้ดเนทีฟของ Android และ iOS โดยไม่ต้องใช้ไลบรารีเนทีฟเพิ่มเติม
ประเด็นสำคัญ
Platform Channel เป็นเทคโนโลยี Flutter ที่ให้การสื่อสารสองทางระหว่างโค้ด Dart ของแอปพลิเคชันและโค้ดเนทีฟของระบบปฏิบัติการ Android และ iOS หากไม่มี Platform Channel แอปพลิเคชัน Flutter จะถูกจำกัดอยู่ที่ความสามารถที่เฟรมเวิร์กมีให้ และไม่สามารถเข้าถึง API กล้อง เซ็นเซอร์ บลูทูธ ระบบไฟล์ และฟังก์ชันระดับต่ำอื่นๆ ของอุปกรณ์ได้โดยตรง
สถาปัตยกรรม Platform Channel สร้างขึ้นบนหลักการของ การแลกเปลี่ยนข้อความแบบอะซิงโครนัส ฝั่ง Dart ส่งคำขอผ่านช่องสัญญาณ ฝั่งเนทีฟประมวลผลและส่งคืนผลลัพธ์ ข้อความทั้งหมดถูกซีเรียลไลซ์เป็นรูปแบบไบนารีและส่งผ่านบัฟเฟอร์ข้อความของ Flutter Engine ซึ่งรับประกันความหน่วงต่ำที่สุดเมื่อถ่ายโอนข้อมูลระหว่างสภาพแวดล้อมการทำงาน
แต่ละ Platform Channel ถูกระบุด้วย ชื่อเชิงตรรกะ ที่ไม่ซ้ำกัน — สตริงที่ทำหน้าที่เป็นที่อยู่สำหรับการกำหนดเส้นทางข้อความ ฝั่ง Dart และฝั่งเนทีฟต้องใช้ชื่อช่องสัญญาณเดียวกันเพื่อให้การสื่อสารถูกตั้งค่าอย่างถูกต้อง Flutter รองรับจำนวนช่องสัญญาณตามต้องการในแอปพลิเคชันเดียว และแต่ละช่องสัญญาณทำงานอย่างอิสระจากช่องอื่นๆ
ตามเอกสารอย่างเป็นทางการของ Flutter Platform Channel ประมวลผลข้อความในลำดับเดียวกับที่ถูกส่ง ซึ่งรับประกัน ลำดับการเรียกที่คาดการณ์ได้ ซึ่งสำคัญอย่างยิ่งสำหรับสถานการณ์ที่ลำดับการประมวลผลส่งผลต่อความถูกต้อง เช่น การเริ่มต้นโมดูลเนทีฟตามลำดับ หรือห่วงโซ่ของการดำเนินการที่ขึ้นต่อกัน
กลไกการส่งข้อความผ่าน Platform Channel ประกอบด้วยสามชั้นหลัก: ฝั่ง Dart ส่งข้อความเป็น Map หรือ List ผ่าน invokeMethod, Flutter Engine ซีเรียลไลซ์โดยใช้ StandardMethodCodec และฝั่งเนทีฟรับการเรียกในตัวจัดการของมัน ผลลัพธ์ถูกส่งกลับตามเส้นทางเดียวกันในทิศทางตรงกันข้าม
กระบวนการซีเรียลไลซ์ แปลงประเภทข้อมูล Dart เป็นค่าที่เทียบเท่าบนแพลตฟอร์มเนทีฟโดยอัตโนมัติ ตัวเลข สตริง ค่าบูลีน รายการ และพจนานุกรมได้รับการสนับสนุนโดยไม่ต้องกำหนดค่าเพิ่มเติมจากนักพัฒนา ประเภทข้อมูลที่กำหนดเองต้องถูกซีเรียลไลซ์ด้วยตนเอง เช่น เป็นสตริง JSON ก่อนส่งผ่านช่องสัญญาณ
ฝั่ง Flutter Engine ข้อความจะเข้าสู่ คิวเธรดหลัก ของแพลตฟอร์มเนทีฟ ใน Android คือเธรดหลักของแอปพลิเคชัน ใน iOS คือลูปการทำงานหลัก ซึ่งหมายความว่าการดำเนินการที่ใช้เวลานานในตัวจัดการช่องสัญญาณจะบล็อกอินเทอร์เฟซผู้ใช้และทำให้เกิดการค้าง นักพัฒนาได้รับคำแนะนำให้ทำงานหนักในเธรดพื้นหลังและส่งคืนผลลัพธ์แบบอะซิงโครนัสผ่าน 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 ออกแบบมาสำหรับสถานการณ์ที่ฝั่งเนทีฟสร้างสตรีมเหตุการณ์อย่างต่อเนื่องตามเวลา ข้อมูลถูกส่งไปยัง Dart ผ่าน Stream ทำให้สามารถสมัครรับการอัปเดตแบบเรียลไทม์ กรณีการใช้งานทั่วไปรวมถึงการอ่านค่า accelerometer, พิกัด GPS, การเปลี่ยนแปลงสถานะ Bluetooth และการแจ้งเตือนจากบริการระบบ
แตกต่างจาก MethodChannel, EventChannel ใช้ โมเดลเผยแพร่-สมัครรับ ฝั่งเนทีฟส่งเหตุการณ์เมื่อเกิดขึ้น โดยไม่ต้องมีการร้องขอจากโค้ด Dart อย่างชัดเจน ผู้สมัครรับบนฝั่ง Dart รับแต่ละเหตุการณ์ในองค์ประกอบสตรีมแยกต่างหาก และสามารถกรองหรือแปลงข้อมูลที่ได้รับก่อนนำไปใช้ในอินเทอร์เฟซ
เมื่อใช้ EventChannel จำเป็นต้องจัดการ การสมัครรับและการยกเลิก อย่างถูกต้อง แต่ละการเรียก StreamSubscription ควรถูกยกเลิกเมื่อการทำงานกับช่องสัญญาณเสร็จสิ้นเพื่อป้องกันการรั่วไหลของหน่วยความจำบนฝั่งเนทีฟ แพลตฟอร์ม Flutter ยกเลิกสตรีมโดยอัตโนมัติเมื่อวิดเจ็ตถูกทำลาย แต่การจัดการการสมัครรับอย่างชัดเจนช่วยเพิ่มความน่าเชื่อถือของแอปพลิเคชันในสถานการณ์ที่ทำงานยาวนาน
BasicMessageChannel เป็นประเภท Platform Channel ที่ยืดหยุ่นที่สุด ออกแบบมาสำหรับการแลกเปลี่ยนข้อความแบบอะซิงโครนัสตามต้องการ แตกต่างจาก MethodChannel ที่แต่ละข้อความประกอบด้วยชื่อเมธอดและอาร์กิวเมนต์ BasicMessageChannel ส่งเฉพาะเพย์โหลดโดยไม่มีการกำหนดเส้นทางในตัว ผู้ส่งส่งข้อความ ผู้รับประมวลผลและส่งคืนการตอบกลับ
ช่องสัญญาณประเภทนี้สะดวกสำหรับ โปรโตคอลการโต้ตอบที่กำหนดเอง ซึ่งโครงสร้างข้อความสามารถเปลี่ยนแปลงได้ตามสถานะของแอปพลิเคชัน BasicMessageChannel ใช้ StandardMessageCodec เป็นค่าเริ่มต้น แต่รองรับการเสียบ MessageCodec ที่กำหนดเองสำหรับรูปแบบการซีเรียลไลซ์ข้อมูลที่ไม่ได้มาตรฐาน
ในทางปฏิบัติ BasicMessageChannel ถูกใช้น้อยกว่า MethodChannel เพราะต้องการการจัดการการกำหนดเส้นทางข้อความด้วยตนเองโดยไม่มีรูปแบบการตั้งชื่อในตัว อย่างไรก็ตาม มันจำเป็นเมื่อรวมกับไลบรารีเนทีฟที่คาดหวังรูปแบบข้อความเฉพาะที่แตกต่างจากรูปแบบคำขอ-ตอบกลับมาตรฐานที่ใช้ใน MethodChannel
มาดูการใช้งาน Platform Channel ในทางปฏิบัติโดยใช้ตัวอย่างการรับระดับแบตเตอรี่ของอุปกรณ์ ตัวอย่างนี้แสดงเวิร์กโฟลว์ทั้งหมด: การประกาศ MethodChannel บนฝั่ง Dart การใช้งานตัวจัดการบน Android และ iOS และการจัดการข้อผิดพลาดอย่างถูกต้องเมื่อข้อมูลไม่พร้อมใช้งานหรือสิทธิ์ที่จำเป็นขาดหาย
บนฝั่ง Dart อินสแตนซ์ของ MethodChannel ถูกสร้างขึ้นด้วยชื่อช่องสัญญาณสตริงที่ไม่ซ้ำกัน เมธอด invokeMethod ส่งคำขอไปยังฝั่งเนทีฟและรอผลลัพธ์เป็น Future การจัดการข้อผิดพลาดทำได้โดยการจับ 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 ตัวจัดการถูกลงทะเบียนใน 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 ตัวจัดการถูกลงทะเบียนในคลาส 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 หรือเมื่อทำงานกับระบบไฟล์ภายนอกแซนด์บ็อกซ์ของแอปพลิเคชัน
สถานการณ์ทั่วไปแรกคือ การใช้ API เนทีฟ ที่ไม่สามารถเข้าถึงได้โดยตรงจาก Dart ซึ่งรวมถึงบริการระบบของ Android และ iOS เซ็นเซอร์ฮาร์ดแวร์ที่มีโปรโตคอลการถ่ายโอนข้อมูลที่ไม่ได้มาตรฐาน การแจ้งเตือนแบบพุชด้วยตรรกะการประมวลผลที่กำหนดเอง และการดำเนินการเข้ารหัสที่ต้องใช้โมดูลความปลอดภัยฮาร์ดแวร์สำหรับการจัดเก็บคีย์อย่างปลอดภัย
สถานการณ์ที่สองคือ การรวมโค้ดเนทีฟที่มีอยู่ เข้ากับโปรเจกต์ 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 และแพลตฟอร์มเนทีฟ สามารถสร้างช่องสัญญาณแยกต่างหากสำหรับโมดูลต่างๆ: หนึ่งสำหรับกล้อง อีกอันสำหรับบลูทูธ อันที่สามสำหรับเซ็นเซอร์ — ทั้งหมดทำงานอย่างอิสระและไม่ส่งผลกระทบต่อประสิทธิภาพของกันและกัน
บนฝั่ง Dart ข้อผิดพลาดถูกจัดการผ่าน PlatformException ซึ่งฝั่งเนทีฟส่งคืนเมื่อเกิดข้อยกเว้น บล็อก try-catch จับข้อยกเว้นและให้การเข้าถึงรหัสข้อผิดพลาด ข้อความ และรายละเอียด บนฝั่งเนทีฟ การเรียก result.error จะส่งข้อผิดพลาดกลับไปยัง Dart เมธอด result.notImplemented ก็พร้อมใช้งานสำหรับเมธอดที่ไม่ได้รับการสนับสนุนโดยช่องสัญญาณ
ใช่ ตัวจัดการ Platform Channel ทำงานบน เธรดหลัก ของแพลตฟอร์มเนทีฟ หากตัวจัดการดำเนินการที่ใช้เวลานาน — คำขอเครือข่าย การอ่านดิสก์ หรือการคำนวณหนัก — อินเทอร์เฟซผู้ใช้อาจค้าง ขอแนะนำให้เรียกใช้งานหนักในเธรดพื้นหลังบนฝั่งเนทีฟและเรียก result หลังจากเสร็จสิ้นเท่านั้น ฝั่ง Dart ไม่ถูกบล็อกเนื่องจากธรรมชาติอะซิงโครนัสของ invokeMethod
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ