Method Channel est un mécanisme de communication bidirectionnelle entre le code Dart et le côté natif d'iOS et d'Android dans Flutter. Selon la Flutter Documentation, 2026, Method Channel permet le transfert de messages typés entre Dart et la plateforme hôte. Sans ce mécanisme, il est impossible d'accéder aux capacités matérielles de l'appareil, aux SDK natifs et aux appels système depuis le code de l'application.
Points clés
Method Channel est le composant central de la couche de plateforme de Flutter, à travers lequel les isolates Dart échangent des messages avec l'application hôte sur iOS ou Android. La tâche principale du canal est de masquer les différences dans les protocoles de transfert de données entre les deux plateformes et de fournir une API unifiée au développeur.
Lorsqu'une application Flutter a besoin d'accéder à la caméra, au Bluetooth, aux capteurs ou à toute autre API native, un appel direct depuis Dart est impossible. Flutter s'exécute dans un moteur construit sur C++ et n'a pas accès aux frameworks UIKit ou Android SDK. Method Channel résout ce problème en créant un pont entre le monde Dart et le monde du code natif.
Selon Google I/O 2024, plus de 80 % des applications Flutter en production utilisent au moins un Method Channel pour l'intégration avec les services de plateforme. Cela confirme le rôle critique du canal dans l'architecture des projets modernes.
Pour le développeur, Method Channel ressemble à un appel de fonction asynchrone ordinaire. Sous le capot, la sérialisation du message, son transfert via le tampon du moteur et l'exécution du code natif sur le thread principal de la plateforme se produisent.
L'interaction via Method Channel commence lorsque le côté Dart envoie un message contenant le nom de la méthode et les arguments. Le Flutter Engine reçoit ce message, le convertit au format standard StandardMethodCodec et le transmet au côté natif via BinaryMessenger.
Le côté natif contient un gestionnaire — MethodCallHandler, qui reçoit l'appel désérialisé et exécute la logique correspondante. Le résultat est renvoyé à Dart sous forme de Response, contenant soit un résultat réussi, soit une erreur avec un code et un message.
Le cycle d'appel complet via Method Channel peut être divisé en six étapes. L'isolate Dart crée une instance de canal avec un nom unique pour identifier la connexion. Lors de l'appel à invokeMethod, le code de plateforme Dart sérialise le nom de la méthode et les arguments à l'aide de MethodCodec, qui les convertit en un tampon binaire via StandardMessageCodec.
Le Flutter Engine transmet ce tampon via un socket au côté natif. Le BinaryMessenger natif lit le message, identifie le canal par son nom et appelle le gestionnaire enregistré, en lui transmettant un objet FlutterMethodCall avec les données analysées. Le gestionnaire exécute le code nécessaire et renvoie un résultat qui parcourt le chemin inverse de sérialisation et arrive dans Dart sous forme de Future.
L'architecture de Method Channel se compose de plusieurs entités interconnectées, chacune responsable de sa propre étape de transfert de données. L'API Dart fournit la classe MethodChannel, qui cache au développeur les détails de bas niveau de la sérialisation et du routage.
BinaryMessenger est une interface de bas niveau du Flutter Engine pour envoyer et recevoir des messages binaires entre Dart et la plateforme hôte. Chaque MethodChannel se lie à un BinaryMessenger spécifique qui assure le routage par nom de canal. Côté Dart, la classe BinaryMessenger est utilisée, sur Android — BinaryMessenger du package io.flutter.embedding.engine, sur iOS — le protocole FlutterBinaryMessenger.
MethodCodec est un encodeur qui convertit les appels de méthode et les valeurs de retour en format binaire. Flutter est livré avec deux implémentations intégrées : StandardMethodCodec (par défaut) et JSONMethodCodec (pour les chaînes JSON). StandardMethodCodec utilise en interne StandardMessageCodec, qui sérialise les données avec la prise en charge de tous les types de base de Dart.
StandardMessageCodec prend en charge un ensemble limité de types de données pour garantir la compatibilité entre Dart, Kotlin et Swift. La liste comprend : null, bool, int, double, String, Uint8List, Int32List, Int64List, Float64List, List et Map avec clés de type chaîne.
Tous les autres types — DateTime, objets DTO ou classes personnalisées — doivent être convertis dans l'un des formats répertoriés. L'approche la plus courante consiste à sérialiser les objets complexes dans une Map avec des champs et à reconstruire la structure côté récepteur à partir d'un dictionnaire de champs.
Pour transférer de grandes données binaires, comme des images de la caméra, Flutter recommande d'utiliser BasicMessageChannel avec Uint8List pour éviter la copie complète du tampon à chaque appel via MethodChannel.
| Type Dart | Type Kotlin | Type 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 |
La configuration de Method Channel côté Android s'effectue dans une classe qui implémente FlutterPlugin, ou directement dans MainActivity. La première approche est recommandée car elle assure une gestion appropriée du cycle de vie du plugin et la compatibilité avec les scénarios add-to-app.
Après avoir créé une instance de canal avec le même nom que côté Dart, il est nécessaire d'enregistrer un MethodCallHandler via setMethodCallHandler. À l'intérieur du gestionnaire, le développeur vérifie le nom de la méthode entrante avec when et renvoie le résultat via result.success ou une erreur via result.error avec un code et un message.
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()
}
}
}
}
Dans cet exemple, le canal nommé samples.flutter.dev/battery traite l'appel getBatteryLevel, obtient le niveau de batterie via Android BatteryManager et le renvoie au code Dart. Le nom du canal doit correspondre des deux côtés, sinon le message n'atteindra pas le gestionnaire.
Pour le code de production, il est recommandé d'isoler la logique de Method Channel dans une classe séparée implémentant FlutterPlugin. Cela permet de réutiliser le plugin entre projets et garantit un nettoyage correct des ressources lors de l'appel à onDetachedFromEngine. Le plugin est enregistré via registerWith et peut être testé indépendamment de l'Activity.
Method Channel sur iOS est configuré dans une classe qui implémente le protocole FlutterPlugin, ou dans AppDelegate. L'approche recommandée est de créer une classe de plugin séparée qui s'enregistre via FlutterPluginRegistrar et est gérée par Flutter Engine.
Le côté Dart envoie un appel, et le gestionnaire natif reçoit un objet FlutterMethodCall avec le nom de la méthode et les arguments. Le développeur détermine la méthode appelée via switch sur call.method et renvoie le résultat via la closure result. Pour accéder aux API iOS, UIKit et d'autres frameworks système sont utilisés.
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)
}
}
}
L'approche FlutterPlugin garantit l'enregistrement et la désactivation corrects du plugin lors de la destruction de Flutter Engine. Dans le gestionnaire Swift, un switch sur call.method est utilisé, chaque cas renvoie un résultat via la closure result. Les arguments sont accessibles via call.arguments avec conversion vers le type approprié.
Lorsqu'on travaille avec Method Channel, il est important de suivre plusieurs règles clés pour garantir les performances et la stabilité de l'application. La recommandation principale est de minimiser la quantité et le volume des données transférées, en particulier lors des appels dans des boucles d'animation ou à haute fréquence.
Côté natif, il faut toujours gérer les exceptions et renvoyer une erreur via result.error avec un message lisible. Côté Dart, chaque appel invokeMethod doit être encapsulé dans try-catch pour intercepter PlatformException. Ignorer les erreurs peut entraîner un plantage inattendu de l'application sans raison claire.
Par défaut, Method Channel exécute le code natif sur le thread principal de la plateforme. Si le gestionnaire effectue une opération lourde, l'exécution doit être déplacée vers un thread d'arrière-plan en utilisant Kotlin Coroutines sur Android ou Grand Central Dispatch sur iOS. Le résultat doit être renvoyé via result uniquement après la fin du travail sur le thread principal.
Choisissez des noms uniques pour les canaux en utilisant la notation de domaine inversé — par exemple, com.example.app/feature. Les noms courts peuvent entrer en conflit avec d'autres plugins. Flutter enregistre les canaux globalement, donc des noms identiques dans différents plugins entraînent l'écrasement du gestionnaire et des appels cassés.
Questions fréquentes
MethodChannel est conçu pour appeler des méthodes selon un modèle requête-réponse avec encodage via MethodCodec. BasicMessageChannel envoie des messages arbitraires sans format de méthode ni d'arguments, ce qui est pratique pour les données en flux et les événements de la plateforme.
Directement — non. StandardMessageCodec ne prend en charge que les types de base : primitifs, String, Uint8List, List et Map. Les objets personnalisés doivent être sérialisés manuellement dans une Map avant l'envoi et reconstruits côté récepteur à partir d'un dictionnaire de champs.
Côté natif, utilisez result.error avec un code d'erreur et un message. Côté Dart, encapsulez invokeMethod dans try-catch et attrapez PlatformException. Si la méthode n'est pas implémentée sur la plateforme, renvoyez result.notImplemented.
Chaque appel effectue une sérialisation et une copie de données entre isolates et plateformes. Pour les appels peu fréquents, la surcharge est négligeable. Lors du transfert de mégaoctets de données par image, des retards et une chute de FPS peuvent se produire. Pour les données en flux, utilisez des vues de plateforme ou des objets de rendu texturé.
Utilisez EventChannel — il est conçu pour diffuser des événements du côté natif vers Dart. La plateforme initie l'envoi via EventSink, et Dart s'abonne au flux avec receiveBroadcastStream. Method Channel n'est pas adapté à ce scénario.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.