قنوات المنصة (MethodChannel)
تواصل مع التعليمات البرمجية الأصلية لنظامَي Android (Kotlin/Java) وiOS (Swift/Objective-C) باستخدام Platform Channels للميزات الخاصة بالمنصة
قنوات المنصة (MethodChannel) درس مجاني في Flutter Mobile Development على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Flutter Mobile Development، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Flutter Mobile Development 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
Platform Channels: Bridging Native
Welcome to Platform Channels! Sometimes, your Flutter app needs to access features specific to the underlying mobile platform (Android or iOS) that aren't available directly in Flutter or Dart.
This is where Platform Channels come in. They act as a bridge, allowing your Dart code to communicate with native code written in Kotlin/Java for Android or Swift/Objective-C for iOS.
How MethodChannel Works
Platform Channels use a system called MethodChannel to send messages between Flutter and the native platform. Think of it as a two-way street:
- Flutter sends method calls to the native side.
- Native code executes the method and sends a result back to Flutter.
Messages are sent asynchronously, ensuring your UI remains responsive.
Flutter: Creating MethodChannel
On the Flutter (Dart) side, you create a MethodChannel instance. The key is to use a unique string identifier, often called the 'channel name', which must be the same on both Flutter and native sides.
Let's set up a channel to get device information:
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Define the MethodChannel with a unique name
static const platform = MethodChannel('com.example.app/device');
String _deviceModel = 'Unknown';
Future<void> _getDeviceModel() async {
// Implementation to invoke native method will be added
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Platform Channel Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Device Model: $_deviceModel'),
ElevatedButton(
onPressed: _getDeviceModel,
child: Text('Get Device Model'),
),
],
),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage()));
}Flutter: Invoking Native Methods
To call a native method, use platform.invokeMethod(). This call is asynchronous and returns a Future. You'll await its result to get the data from the native side.
We'll update our _getDeviceModel method:
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static const platform = MethodChannel('com.example.app/device');
String _deviceModel = 'Unknown';
Future<void> _getDeviceModel() async {
String deviceModel;
try {
// Invoking the native method 'getDeviceModel'
final String result = await platform.invokeMethod('getDeviceModel');
deviceModel = result; // The result from native is a String
} on PlatformException catch (e) {
// Catch errors sent back from the native side
deviceModel = "Failed to get model: '${e.message}'.";
}
setState(() {
_deviceModel = deviceModel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Platform Channel Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Device Model: $_deviceModel'),
ElevatedButton(
onPressed: _getDeviceModel,
child: Text('Get Device Model'),
),
],
),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage()));
}Flutter: Sending Data to Native
You can also pass arguments to native methods. These arguments are sent as a Map, allowing you to include various data types.
Let's create a new method to get a 'custom' device model with a prefix and suffix:
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static const platform = MethodChannel('com.example.app/device');
String _customModel = 'Unknown';
Future<void> _getCustomModel(String prefix) async {
String customModel;
try {
// Invoking with arguments: a Map
final String result = await platform.invokeMethod(
'getCustomModel',
{'prefix': prefix, 'suffix': 'v1'}
);
customModel = result;
} on PlatformException catch (e) {
customModel = "Failed to get custom model: '${e.message}'.";
}
setState(() {
_customModel = customModel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Platform Channel Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Custom Model: $_customModel'),
ElevatedButton(
onPressed: () => _getCustomModel('MyApp'),
child: Text('Get Custom Model'),
),
],
),
),
);
}
}
void main() {
runApp(MaterialApp(home: HomePage()));
}Android (Kotlin): Setting Up Channel
On the Android side, you'll set up the MethodChannel in your MainActivity.kt file. This is where you'll define how to handle incoming method calls from Flutter.
Ensure your channel name matches the one in your Flutter code.
package com.example.coddy_app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/device"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This block will execute when Flutter invokes a method
call, result ->
// Logic to handle 'call' and respond using 'result'
}
}
}Android (Kotlin): Handling Calls
Inside the setMethodCallHandler, you check the call.method string to identify which method Flutter is trying to invoke. You can also access arguments using call.argument().
Use result.success() to send data back or result.notImplemented() if the method isn't handled.
package com.example.coddy_app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.os.Build // Import for device info
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/device"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getDeviceModel") {
val model = Build.MODEL // Get device model
result.success(model) // Return success to Flutter
} else if (call.method == "getCustomModel") {
val prefix = call.argument<String>("prefix") ?: ""
val suffix = call.argument<String>("suffix") ?: ""
val customModel = "$prefix-${Build.MODEL}-$suffix"
result.success(customModel)
}
else {
result.notImplemented() // Indicate method not found
}
}
}
}iOS (Swift): Setting Up Channel
For iOS, you'll typically configure the MethodChannel in your AppDelegate.swift file. Similar to Android, the channel name must match the Flutter side.
The setup involves getting the FlutterViewController and creating a FlutterMethodChannel.
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let deviceChannel = FlutterMethodChannel(name: "com.example.app/device",
binaryMessenger: controller.binaryMessenger)
deviceChannel.setMethodCallHandler({ // Set up handler for incoming calls
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Logic to handle 'call' and respond using 'result'
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}iOS (Swift): Handling Calls
In Swift, you use a switch statement or if/else if to check call.method. Arguments are accessed via call.arguments, which is typically a [String: Any] dictionary.
Use result() with the value for success or FlutterMethodNotImplemented for unhandled methods.
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let deviceChannel = FlutterMethodChannel(name: "com.example.app/device",
binaryMessenger: controller.binaryMessenger)
deviceChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getDeviceModel" {
let model = UIDevice.current.model // Get device model
result(model) // Return success to Flutter
} else if call.method == "getCustomModel" {
if let args = call.arguments as? [String: Any],
let prefix = args["prefix"] as? String,
let suffix = args["suffix"] as? String {
let customModel = "\(prefix)-\(UIDevice.current.model)-\(suffix)"
result(customModel)
} else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Arguments missing or invalid", details: nil))
}
}
else {
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}Handling Native Errors
It's crucial to handle errors from the native side gracefully. Native code can send back an error using result.error() (Android) or FlutterError() (iOS).
Flutter catches these as a PlatformException, which you can handle in a try-on block, as shown in our Flutter invocation examples.
package com.example.coddy_app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.os.Build
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/device"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getCustomModel") {
val prefix = call.argument<String>("prefix")
if (prefix == null || prefix.isEmpty()) {
// Sending an error back to Flutter
result.error("MISSING_PREFIX", "Prefix argument is required.", null)
} else {
val suffix = call.argument<String>("suffix") ?: ""
val customModel = "$prefix-${Build.MODEL}-$suffix"
result.success(customModel)
}
}
// ... other methods
else { result.notImplemented() }
}
}
}Platform Channel Check
Test your understanding of Flutter's Platform Channels.
Recap: Bridging the Gap
Great job! You've learned how to use Platform Channels to extend your Flutter app's capabilities by interacting with native platform-specific features.
- You create a
MethodChannelin Flutter and on each native platform (Android/iOS). - Flutter uses
invokeMethod()to call native functions. - Native code handles these calls and returns results or errors.
This powerful technique ensures your Flutter apps are never limited by what the framework provides directly!
الأسئلة الشائعة
هل درس «قنوات المنصة (MethodChannel)» مجاني؟
نعم — نص درس «قنوات المنصة (MethodChannel)» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Flutter Mobile Development، انتقل إلى CoddyKit PRO. تتضمن دورة Flutter Mobile Development 4 دروس في المجموع.
ماذا ستتعلم في «قنوات المنصة (MethodChannel)»؟
تواصل مع التعليمات البرمجية الأصلية لنظامَي Android (Kotlin/Java) وiOS (Swift/Objective-C) باستخدام Platform Channels للميزات الخاصة بالمنصة تتمرن على Flutter Mobile Development مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Flutter Mobile Development؟
لا تُشترط خبرة سابقة. Flutter Mobile Development على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «قنوات المنصة (MethodChannel)»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Flutter Mobile Development هذا؟
نعم. كل درس في Flutter Mobile Development يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- قنوات المنصة (MethodChannel)
- الموقع الجغرافي وإضافات الكاميرا
- دمج واجهة المستخدم الأصلية
- الأذونات والمستشعرات