Escribiendo código personalizado, específico de plataforma
- Visión general de la arquitectura: platform channels
- Ejemplo: Llamando código especifico de plataforma iOS y Android usando platform channels
- Step 1: Crea un nuevo proyecto de app
- Step 2: Crea un platform client de Flutter
- Paso 3a: Añade la implementación especifica de plataforma Android usando Java
- Paso 3b: Añadir una implementación espéfica de plataforma Android usando Kotlin
- Paso 4a: Añade la implementación específica de la plataforma iOS usando Objective-C
- Paso 4b: Añade la implementación específica de la plataforma iOS usando Swift
- Separa código específico de la plataforma del código de UI
- Publica código específico de plataforma como un paquete
- Custom channels y codecs
Esta guía describe como escribir código personalizado, específico de plataforma. Algunas funcionalidades específicas de plataforma están disponibles a través de los paquetes existentes; por favor mire usando paquetes.
Flutter usa un sistema flexible que permite llamar APIs específicas de plataforma ya esté disponible en código Java o Kotlin en Android, o en código Objective-C o Swift en iOS.
El soporte de APIs específicas de plataforma de Flutter, no está relacionado con la generación de código, sino más bien con un estilo flexible de paso de mensajes:
-
La parte Flutter de tu app envía mensajes a su host, la parte iOS o Android de tu app, usando platform channel.
-
El host escucha el platform channel, y recibe el mensaje. Esto entonces permite llamar a cualquier número de APIs específicas de plataforma—usando el lenguaje de programación nativo—y devuelve una respuesta al cliente, la parte Flutter de tu app.
Visión general de la arquitectura: platform channels
Los mensajes son pasados entre el cliente (UI) y el host (plataforma) usando platform channels como se ilustra en este diagrama:

Los mensajes y las respuestas son pasados de forma asíncrona, para asegurar que el interfaz de usuario permanece responsivo.
En el lado del cliente, la (API) MethodChannel permite enviar
mensajes que corresponden con llamadas a métodos. En el lado de la plataforma, la
(API) MethodChannel en Android y la (API)
FlutterMethodChannel en iOS permiten recibir llamadas a métodos y devolver
un resultado. Estas clases te permiten desarrollar un plugin de plataforma con
un código ‘boilerplate’ verdaderamente pequeño.
Nota: Si se desea, las llamadas a métodos pueden también ser enviadas en la
dirección inversa, con la plataforma actuando como cliente de métodos implementados
en Dart. Un ejemplo concreto de esto es el plugin quick_actions.
Tipos de datos y codecs soportados por Platform channel
Eñ platform channels estándar usa un codec de mensaje estándar que soporta
serialización binaria eficiente de valores similares a JSON simples, como booleans,
numbers, Strings, byte buffers, y List y Maps de estos (mira
StandardMessageCodec)
para detalles. La serialización y deserialización de estos valores hacia y desde los
mensajes ocurre automáticamente cuando envías y recibes valores.
La siguiente tabla muestra como son recibidos los valores Dart en la plataforma y viceversa:
| Dart | Android | iOS |
|---|---|---|
| null | null | nil (NSNull cuando están anidados) |
| bool | java.lang.Boolean | NSNumber numberWithBool: |
| int | java.lang.Integer | NSNumber numberWithInt: |
| int, si 32 bits no son suficientes | java.lang.Long | NSNumber numberWithLong: |
| double | java.lang.Double | NSNumber numberWithDouble: |
| String | java.lang.String | NSString |
| Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
| Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
| Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
| Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
| List | java.util.ArrayList | NSArray |
| Map | java.util.HashMap | NSDictionary |
Ejemplo: Llamando código especifico de plataforma iOS y Android usando platform channels
Lo siguiente demuestra como llamar una API especifica de plataforma para obtener y
mostrar el nivel de batería actual. Usa la API BatteryManager de Android, y
la API device.batteryLevel de iOS, vía un único mensaje de plataforma,
getBatteryLevel.
El ejemplo añade el código especifico de plataforma dentro del método main de la app. Si quieres reutilizar el código específico de plataforma para múltiples aplicaciones, el paso de creación del proyecto es ligeramente diferente (mira desarrollando paquetes), pero el código del platform channel se escribe de la misma manera.
Nota: El código fuente ejecutable completo para este ejemplo, está disponible en
/examples/platform_channel/
para Android con Java y iOS con Objective-C. Para iOS con Swift, mira
/examples/platform_channel_swift/.
Step 1: Crea un nuevo proyecto de app
Empieza creando una nueva app:
- En un terminal ejecuta:
flutter create batterylevel
Por defecto nuestra plantilla soporta escribir código Android usando Java , o código iOS
usando Objective-C. Para usar Kotlin o Swift, usa el flag -i y/o -a:
- En un terminal ejecuta:
flutter create -i swift -a kotlin batterylevel
Step 2: Crea un platform client de Flutter
La clase State de la app conserva el estado actual de la app. Necesitamos ampliar esto
para conservar el estado actual de la batería.
Primero, construimos el channel. Usamos un MethodChannel con un único
método de plataforma que devuelve el nivel de batería.
Los lados cliente y host de un channel son conectados a través de un channel name
pasado en el constructor del channel. Todos los channel names usados en una app deben
ser únicos; recomendamos prefijar el channel name con un ‘prefijo de dominio’ único’,
ej. samples.flutter.io/battery.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.io/battery');
// Obtiene el nivel de batería.
}
A continuación, invocamos un método en el method channel, especificando el método concreto
a llamar vía un String identificador getBatteryLevel. La llamada puede fallar—por
ejemplo si la plataforma no soporta la API de plataforma (si estamos ejecutando en
un simulador), entonces envolveremos la llamada a invokeMethod en una declaración try-catch.
Usamos el resultado devuelto para actualizar el estado de el interfaz de usuario en
_batteryLevel dentro de setState.
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Finalmente, reemplazamos el método build de la plantilla para contener una
pequeña interfaz de usuario que muestra el estado de la batería en un string,
y un botón para refrescar el valor.
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel),
],
),
),
);
}
Paso 3a: Añade la implementación especifica de plataforma Android usando Java
Nota: El siguiente paso usa Java. Si prefieres Kotlin, salta al paso 3b.
Empieza abriendo la parte host de Android de tu app Flutter en Android Studio:
-
Inicia Android Studio
-
Selecciona el menú ‘File > Open…’
-
Navega al directorio que contiene tu app Flutter, y selecciona la carpeta
androiddentro de él. Haz clic en OK. -
Abre el fichero
MainActivity.javalocalizado en la carpetajavaen la vista de Proyecto.
A continuación, crea un MethodChannel y establece un MethodCallHandler dentro del
método onCreate. Asegúrate de usar el mismo channel name que fue usado en el
lado del cliente en Flutter.
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.io/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
// TODO
}
});
}
}
A continuación, añadimos el código actual Android en Java, que usa la API Android battery para obtener el nivel de la batería. Este código es exactamente el mismo que habrías escrito en una app nativa Android.
Primero, añade los imports necesarios al principio del fichero:
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
Entonces añade lo siguiente como un método en la clase activity, debajo del método
onCreate:
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
Finalmente, completamos el método onMethodCall que añadimos anteriormente. Necesitas
manejar un único método de plataforma, getBatteryLevel, vamos a probar esto en el
argumento call. La implementación de este método de plataforma simplemente llama al
código Android escrito en el paso anterior, y pasa de vuelta una respuesta para
ambos casos, éxito y error, usando el argumento response. En cambio, si se llama
un método desconocido, reportaremos esto. Reemplaza:
public void onMethodCall(MethodCall call, Result result) {
// TODO
}
con:
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
Deberías ahora poder ejecutar la app en Android. Si estas usando el emulador de Android,
puedes ajustar el nivel de batería en el panel Extended Controls
accesible desde el botón ... en la barra de herramientas.
Paso 3b: Añadir una implementación espéfica de plataforma Android usando Kotlin
Nota: Los siguientes pasos son similares al paso 3a, solo usaremos Kotlin en lugar de Java.
Este paso asume que has creado tu proyecto en el paso 1.
usando la opción -a kotlin.
Empieza abriendo la parte host Android de tu app Flutter en Android Studio:
-
Inicia Android Studio
-
Selecciona el menú ‘File > Open…’
-
Navega hasta el directorio que contiene tu app Flutter, y selecciona la carpeta
androiddentro de él. Haz Clic en OK. -
Abre el fichero
MainActivity.ktlocalizado en la carpetakotlinen la Project view. (Nota: Si estas editando usando Android Studio 2.3, nota que la carpeta ‘kotlin’ se muestra como si se llamara ‘java’.)
A continuación, dentro del método onCreate, crea un MethodChannel y llama a
setMethodCallHandler. Asegúrate que usas el mismo channel name que fue usado
la parte cliente en Flutter.
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity() : FlutterActivity() {
private val CHANNEL = "samples.flutter.io/battery"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
// TODO
}
}
}
A continuación, añade el código actual Kotlin de Android que usa la API Android battery para obtener el nivel de batería. Este código es exactamente el mismo que escribirías en una app Android nativa.
Primero, añade los imports necesarios al principio del fichero:
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
A continuación, añade lo siguiente como un nuevo método en la clase MainActivity,
debajo del método onCreate:
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
Finalmente, completa el método onMethodCall que añadimos anteriormente. Necesitamos
manejar un único método de plataforma, getBatteryLevel, entonces probamos esto en el
argumento call. La implementación de ese método de plataforma simplemente llama al código
Android escrito en el paso anterior, y pasamos de vuelta una respuesta para
ambos casos, éxito y error, usando el argumento response. En cambio, si se llama
un método desconocido, reporta esto. Reemplaza:
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
// TODO
}
with:
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
Deberías poder ejecutar tu app en Android. Si estas usando el emulador de Android,
puedes ajustar el nivel de batería en el panel Extended Controls
accesible desde el botón ... en la barra de herramientas.
Paso 4a: Añade la implementación específica de la plataforma iOS usando Objective-C
Nota: Los siguientes pasos usan Objective-C. Si prefieres Swift, salta al paso 4b.
Empieza abriendo la parte iOS de tu app Flutter en Xcode:
-
Inicia Xcode
-
Selecciona el menú ‘File > Open…’
-
Navega hasta el directorio que contiene tu app Flutter, y selecciona la carpeta
iosdentro de él. Haz Clic en OK. -
Asegúrate que el proyecto Xcode compila sin errores.
-
Abre el fichero
AppDelegate.msituado bajo Runner > Runner en el navegador Project.
A continuación, crea un FlutterMethodChannel y añade un manejador dentro del método application
didFinishLaunchingWithOptions:. Asegúrate de usar el mismo channel name
que fue usado en la parte del cliente en Flutter.
#import <Flutter/Flutter.h>
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* batteryChannel = [FlutterMethodChannel
methodChannelWithName:@"samples.flutter.io/battery"
binaryMessenger:controller];
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
// TODO
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
A continuación, añade el código actual ObjectiveC en iOS que usa la API iOS battery para obtener el nivel de batería. Este código es exactamente el mismo que habrías escrito en una app nativa iOS.
Añade lo siguiente como un nuevo método en la clase AppDelegate, justo antes de @end:
- (int)getBatteryLevel {
UIDevice* device = UIDevice.currentDevice;
device.batteryMonitoringEnabled = YES;
if (device.batteryState == UIDeviceBatteryStateUnknown) {
return -1;
} else {
return (int)(device.batteryLevel * 100);
}
}
Finalmente, completa el método setMethodCallHandler añadido anteriormente. Necesitas
manejar un único método de plataforma, getBatteryLevel, entonces probamos esto en el
argumento call. La implementación de este método de plataforma simplemente llama al
código iOS escrito en el paso anterior, y pasa de vuelta una respuesta para ambos
casos, éxito y error, usando el argumento result. En cambio, si se llama
un método desconocido, reporta esto. Reemplaza:
__weak typeof(self) weakSelf = self
[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"getBatteryLevel" isEqualToString:call.method]) {
int batteryLevel = [weakSelf getBatteryLevel];
if (batteryLevel == -1) {
result([FlutterError errorWithCode:@"UNAVAILABLE"
message:@"Battery info unavailable"
details:nil]);
} else {
result(@(batteryLevel));
}
} else {
result(FlutterMethodNotImplemented);
}
}];
Ahora deberías poder ejecutar la app en iOS. Si estas usando el simulador de iOS, nota que este no soporta la API battery, y la app mostrara ‘battery info unavailable’.
Paso 4b: Añade la implementación específica de la plataforma iOS usando Swift
Nota: Los siguientes pasos son similares al paso 4a, solo usando Swift en lugar de Objective-C.
Este paso asume que has creado tu proyecto en el paso 1.
usando la opción -i swift.
Empieza abriendo la parte iOS de tu app Flutter en Xcode:
-
Inicia Xcode
-
Selecciona el menú ‘File > Open…’
-
Navega hasta el directorio que contiene tu app Flutter, y selecciona la carpeta
iosdentro de él. Haz Clic en OK.
A continuación, añade soporte para Swift en la configuración de la plantilla standard que usa Objective-C:
-
Expande Runner > Runner en Project navigator.
-
Abre el fichero
AppDelegate.swiftlocalizado bajo Runner > Runner en Project navigator.
A continuación, sobrescribe la función application:didFinishLaunchingWithOptions: y
crea un FlutterMethodChannel atado al channel name samples.flutter.io/battery:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.io/battery",
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: FlutterResult) -> Void in
// Maneja mensajes sobre la batería.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
A continuación, añadimos el código actual Swift en iOS que usa la API iOS battery para obtener el nivel de batería. Este código es exactamente el mismo que habrías escrito en una app nativa iOS.
Añade lo siguiente como un nuevo método al final de AppDelegate.swift:
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDeviceBatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery info unavailable",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
Finalmente, completa el método setMethodCallHandler añadido anteriormente. Necesitas
manejar un único método de plataforma, getBatteryLevel, entonces probamos esto en el
argumento call. La implementación de este método de plataforma simplemente llama al
código iOS escrito en el paso anterior.Si se llama un método desconocido,
reportaremos esto. Reemplaza:
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
Ahora deberiás poder ejecutar la app en iOS. Si estas usando el simulador de iOS, nota que este no soporta la API battery, y la app muestra ‘battery info unavailable’.
Separa código específico de la plataforma del código de UI
Si esperas usar tu código específico de plataforma en múltiples aplicaciones Flutter, puede ser útil separar el código en un plugin de plataforma situado en un directorio fuera de tu aplicación. Mira deserrollando paquetes para los detalles.
Publica código específico de plataforma como un paquete
Si deseas compartir tu paquete específico de plataforma con otros desarrolladores en el ecosistema Flutter, por favor mira publishing packages para los detalles.
Custom channels y codecs
Además de MethodChannel mencionado anteriormente, también puedes usar el básico
BasicMessageChannel, que soporta paso de mensajes asíncronos
básicos, usando un codec de mensajes personalizado. Además, puedes usar las clases
especializadas BinaryCodec, StringCodec, y
JSONMessageCodec, o crear tu propio codec.

