Introducción
Los canales de plataforma en Flutter permiten la comunicación entre el código Dart y el código nativo (Java/Kotlin para Android y Objective-C/Swift para iOS). Esto es útil cuando necesitas acceder a funcionalidades específicas del sistema operativo que no están disponibles directamente en Flutter.
Conceptos Clave
- Canal de Plataforma (Platform Channel): Es el medio de comunicación entre Flutter y el código nativo.
- Método de Canal (Method Channel): Permite invocar métodos nativos desde Dart y viceversa.
- Canal de Eventos (Event Channel): Permite recibir flujos de datos desde el código nativo hacia Dart.
- Canal Básico (Basic Message Channel): Permite el intercambio de mensajes arbitrarios entre Dart y el código nativo.
Estructura de un Canal de Plataforma
Dart (Flutter)
import 'package:flutter/services.dart';
class PlatformChannelExample {
static const platform = MethodChannel('com.example.platform_channel');
Future<String> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return 'Battery level is $result%.';
} on PlatformException catch (e) {
return "Failed to get battery level: '${e.message}'.";
}
}
}Android (Java)
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.platform_channel";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, 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();
}
}
);
}
private int getBatteryLevel() {
int batteryLevel = -1;
// Código para obtener el nivel de batería
return batteryLevel;
}
}iOS (Swift)
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private let CHANNEL = "com.example.platform_channel"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getBatteryLevel" {
self.receiveBatteryLevel(result: result)
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
}Ejercicio Práctico
Objetivo
Crear una aplicación Flutter que obtenga el nivel de batería del dispositivo utilizando canales de plataforma.
Pasos
-
Configurar el Proyecto Flutter:
- Crear un nuevo proyecto Flutter.
- Añadir el código Dart para invocar el método nativo.
-
Implementar el Código Nativo:
- Para Android, añadir el código Java/Kotlin en
MainActivity. - Para iOS, añadir el código Swift/Objective-C en
AppDelegate.
- Para Android, añadir el código Java/Kotlin en
-
Probar la Aplicación:
- Ejecutar la aplicación en un dispositivo físico o emulador.
- Verificar que el nivel de batería se muestra correctamente.
Código Completo
Dart (Flutter)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BatteryLevelScreen(),
);
}
}
class BatteryLevelScreen extends StatefulWidget {
@override
_BatteryLevelScreenState createState() => _BatteryLevelScreenState();
}
class _BatteryLevelScreenState extends State<BatteryLevelScreen> {
static const platform = MethodChannel('com.example.platform_channel');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level is $result%.';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Battery Level'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_batteryLevel),
SizedBox(height: 20),
ElevatedButton(
onPressed: _getBatteryLevel,
child: Text('Get Battery Level'),
),
],
),
),
);
}
}Android (Java)
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.platform_channel";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, 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();
}
}
);
}
private int getBatteryLevel() {
int batteryLevel = -1;
// Código para obtener el nivel de batería
return batteryLevel;
}
}iOS (Swift)
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
private let CHANNEL = "com.example.platform_channel"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getBatteryLevel" {
self.receiveBatteryLevel(result: result)
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
}Retroalimentación y Consejos
-
Errores Comunes:
- Asegúrate de que el nombre del canal sea el mismo en Dart y en el código nativo.
- Verifica que los permisos necesarios estén configurados en el archivo
AndroidManifest.xmlyInfo.plistpara acceder a la batería. - Si usas un emulador, ten en cuenta que algunos emuladores pueden no soportar la monitorización de la batería.
-
Consejos Adicionales:
- Utiliza
try-catchen Dart para manejar excepciones y proporcionar retroalimentación al usuario. - Mantén el código nativo lo más simple posible y delega la lógica compleja a Dart cuando sea posible.
- Utiliza
Conclusión
En esta sección, aprendiste cómo utilizar los canales de plataforma para comunicarte entre Flutter y el código nativo. Esto te permite acceder a funcionalidades específicas del sistema operativo y extender las capacidades de tus aplicaciones Flutter. En el siguiente módulo, exploraremos cómo gestionar el estado en Flutter para crear aplicaciones más dinámicas y reactivas.
Curso de Desarrollo con Flutter
Módulo 1: Introducción a Flutter
- ¿Qué es Flutter?
- Configuración del Entorno de Desarrollo
- Entendiendo la Arquitectura de Flutter
- Creando Tu Primera App con Flutter
Módulo 2: Conceptos Básicos de Programación en Dart
- Introducción a Dart
- Variables y Tipos de Datos
- Sentencias de Control de Flujo
- Funciones y Métodos
- Programación Orientada a Objetos en Dart
Módulo 3: Widgets en Flutter
- Introducción a los Widgets
- Widgets Stateless vs Stateful
- Widgets Básicos
- Widgets de Diseño
- Widgets de Entrada y Formularios
Módulo 4: Gestión de Estado
Módulo 5: Navegación y Enrutamiento
- Introducción a la Navegación
- Navegación Básica
- Rutas Nombradas
- Pasando Datos Entre Pantallas
- Deep Linking
Módulo 6: Redes y APIs
- Obteniendo Datos de Internet
- Parseo de Datos JSON
- Manejo de Errores de Red
- Usando APIs REST
- Integración con GraphQL
Módulo 7: Persistencia y Almacenamiento
- Introducción a la Persistencia
- Preferencias Compartidas
- Almacenamiento de Archivos
- Base de Datos SQLite
- Usando Hive para Almacenamiento Local
Módulo 8: Conceptos Avanzados de Flutter
- Animaciones en Flutter
- Custom Paint y Canvas
- Canales de Plataforma
- Isolates y Concurrencia
- Optimización de Rendimiento
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias
- Pruebas de Widgets
- Pruebas de Integración
- Técnicas de Depuración
Módulo 10: Despliegue y Mantenimiento
- Preparación para el Lanzamiento
- Construcción para iOS
- Construcción para Android
- Integración Continua/Despliegue Continuo (CI/CD)
- Mantenimiento y Actualización de Tu App
