Method Channel é um mecanismo de comunicação bidirecional entre o código Dart e o lado nativo do iOS e Android no Flutter. De acordo com a Flutter Documentation, 2026, o Method Channel permite a transferência de mensagens tipadas entre o Dart e a plataforma anfitriã. Sem esse mecanismo, é impossível aceder às capacidades de hardware do dispositivo, SDKs nativos e chamadas de sistema a partir do código da aplicação.
Principais pontos
Method Channel é o componente central da camada de plataforma do Flutter, através do qual os isolates Dart trocam mensagens com a aplicação anfitriã no iOS ou Android. A principal tarefa do canal é ocultar as diferenças nos protocolos de transferência de dados entre as duas plataformas e fornecer uma API unificada para o desenvolvedor.
Quando uma aplicação Flutter precisa de aceder à câmara, Bluetooth, sensores ou qualquer outra API nativa, uma chamada direta a partir do Dart é impossível. O Flutter é executado num motor construído em C++ e não tem acesso aos frameworks UIKit ou Android SDK. O Method Channel resolve este problema criando uma ponte entre o mundo Dart e o mundo do código nativo.
Segundo o Google I/O 2024, mais de 80% das aplicações Flutter em produção utilizam pelo menos um Method Channel para integração com serviços de plataforma. Isto confirma o papel crítico do canal na arquitetura de projetos modernos.
Para o desenvolvedor, o Method Channel parece uma chamada de função assíncrona normal. Por baixo dos panos, ocorre a serialização da mensagem, a sua transferência através do buffer do motor e a execução do código nativo na thread principal da plataforma.
A interação através do Method Channel começa quando o lado Dart envia uma mensagem contendo o nome do método e os argumentos. O Flutter Engine recebe esta mensagem, converte-a para o formato padrão StandardMethodCodec e passa-a ao lado nativo através do BinaryMessenger.
O lado nativo contém um manipulador — MethodCallHandler, que recebe a chamada desserializada e executa a lógica correspondente. O resultado é devolvido ao Dart como uma Response, contendo um resultado bem-sucedido ou um erro com código e mensagem.
Todo o ciclo de chamada através do Method Channel pode ser dividido em seis etapas. O isolate Dart cria uma instância do canal com um nome único para identificação da ligação. Ao chamar invokeMethod, o código de plataforma Dart serializa o nome do método e os argumentos usando MethodCodec, que os converte num buffer binário através do StandardMessageCodec.
O Flutter Engine passa este buffer através de um socket para o lado nativo. O BinaryMessenger nativo lê a mensagem, identifica o canal pelo nome e chama o manipulador registado, passando-lhe um objeto FlutterMethodCall com dados analisados. O manipulador executa o código necessário e devolve um resultado, que percorre o caminho inverso de serialização e chega ao Dart como um Future.
A arquitetura do Method Channel consiste em várias entidades interligadas, cada uma responsável pela sua etapa de transferência de dados. A API Dart fornece a classe MethodChannel, que oculta ao desenvolvedor os detalhes de baixo nível da serialização e encaminhamento.
BinaryMessenger é uma interface de baixo nível do Flutter Engine para enviar e receber mensagens binárias entre o Dart e a plataforma anfitriã. Cada MethodChannel liga-se a um BinaryMessenger específico que fornece encaminhamento por nome de canal. No lado Dart é usada a classe BinaryMessenger, no Android — o BinaryMessenger do pacote io.flutter.embedding.engine, no iOS — o protocolo FlutterBinaryMessenger.
MethodCodec é um codificador que converte chamadas de método e valores de retorno em formato binário. O Flutter vem com duas implementações incorporadas: StandardMethodCodec (padrão) e JSONMethodCodec (para strings JSON). O StandardMethodCodec usa internamente o StandardMessageCodec, que serializa dados com suporte para todos os tipos básicos do Dart.
StandardMessageCodec suporta um conjunto limitado de tipos de dados para garantir compatibilidade entre Dart, Kotlin e Swift. A lista inclui: null, bool, int, double, String, Uint8List, Int32List, Int64List, Float64List, List e Map com chaves de string.
Todos os outros tipos — DateTime, objetos DTO ou classes personalizadas — devem ser convertidos para um dos formatos listados. A abordagem mais comum é serializar objetos complexos num Map com campos e reconstruir a estrutura no lado recetor a partir de um dicionário de campos.
Para transferir grandes dados binários, como imagens da câmara, o Flutter recomenda usar BasicMessageChannel com Uint8List para evitar a cópia completa do buffer em cada chamada através do MethodChannel.
| Tipo Dart | Tipo Kotlin | Tipo 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 |
A configuração do Method Channel no lado Android é feita numa classe que implementa FlutterPlugin, ou diretamente na MainActivity. A primeira abordagem é recomendada, pois fornece uma gestão adequada do ciclo de vida do plugin e compatibilidade com cenários add-to-app.
Após criar uma instância do canal com o mesmo nome que no lado Dart, é necessário registar um MethodCallHandler através de setMethodCallHandler. Dentro do manipulador, o desenvolvedor verifica o nome do método recebido usando when e devolve o resultado através de result.success ou um erro através de result.error com código e mensagem.
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()
}
}
}
}
Neste exemplo, o canal chamado samples.flutter.dev/battery trata a chamada getBatteryLevel, obtém o nível da bateria através do Android BatteryManager e devolve-o ao código Dart. O nome do canal deve coincidir em ambos os lados, caso contrário a mensagem não chegará ao manipulador.
Para código de produção, é recomendado isolar a lógica do Method Channel numa classe separada que implemente FlutterPlugin. Isto permite reutilizar o plugin entre projetos e garante a limpeza correta de recursos ao chamar onDetachedFromEngine. O plugin é registado através de registerWith e pode ser testado isoladamente da Activity.
O Method Channel no iOS é configurado numa classe que implementa o protocolo FlutterPlugin, ou no AppDelegate. A abordagem recomendada é criar uma classe de plugin separada que se regista através do FlutterPluginRegistrar e é gerida pelo Flutter Engine.
O lado Dart envia uma chamada, e o manipulador nativo recebe um objeto FlutterMethodCall com o nome do método e os argumentos. O desenvolvedor determina o método chamado através de switch em call.method e devolve o resultado através do closure result. Para aceder às APIs iOS, são utilizados UIKit e outros frameworks do sistema.
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)
}
}
}
A abordagem FlutterPlugin garante o registo e a desativação corretos do plugin quando o Flutter Engine é destruído. No manipulador Swift, é usado switch em call.method, cada caso devolve um resultado através do closure result. Os argumentos estão disponíveis através de call.arguments com conversão para o tipo apropriado.
Ao trabalhar com Method Channel, é importante seguir várias regras fundamentais para garantir o desempenho e a estabilidade da aplicação. A principal recomendação é minimizar a quantidade e o volume de dados transferidos, especialmente em chamadas em ciclos de animação ou com alta frequência.
No lado nativo, deve-se sempre tratar exceções e devolver um erro através de result.error com uma mensagem legível. No lado Dart, cada chamada invokeMethod deve ser envolvida em try-catch para capturar PlatformException. Ignorar erros pode levar a falhas inesperadas da aplicação sem uma razão clara.
Por padrão, o Method Channel executa código nativo na thread principal da plataforma. Se o manipulador realizar uma operação pesada, a execução deve ser movida para uma thread de fundo usando Kotlin Coroutines no Android ou Grand Central Dispatch no iOS. O resultado deve ser devolvido através de result apenas após a conclusão do trabalho na thread principal.
Escolha nomes únicos para os canais usando notação de domínio inverso — por exemplo, com.example.app/feature. Nomes curtos podem entrar em conflito com outros plugins. O Flutter regista os canais globalmente, portanto nomes idênticos em plugins diferentes levam à sobrescrita do manipulador e chamadas quebradas.
Perguntas frequentes
MethodChannel é projetado para chamar métodos num padrão de solicitação-resposta com codificação através de MethodCodec. O BasicMessageChannel envia mensagens arbitrárias sem formato de método e argumentos, o que é conveniente para dados em fluxo e eventos da plataforma.
Diretamente — não. O StandardMessageCodec só suporta tipos básicos: primitivos, String, Uint8List, List e Map. Objetos personalizados devem ser serializados manualmente num Map antes do envio e reconstruídos no lado recetor a partir de um dicionário de campos.
No lado nativo, use result.error com um código de erro e mensagem. No lado Dart, envolva invokeMethod em try-catch e capture PlatformException. Se o método não estiver implementado na plataforma, devolva result.notImplemented.
Cada chamada realiza serialização e cópia de dados entre isolates e plataformas. Para chamadas pouco frequentes, a sobrecarga é insignificante. Ao transferir megabytes de dados por frame, podem ocorrer atrasos e queda de FPS. Para dados em fluxo, use vistas de plataforma ou objetos de renderização com textura.
Use EventChannel — ele é projetado para transmitir eventos do lado nativo para o Dart. A plataforma inicia o envio através de EventSink, e o Dart subscreve o fluxo usando receiveBroadcastStream. O Method Channel não é adequado para este cenário.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.