Els canals de plataforma (Platform Channels) són una característica poderosa de Flutter que permet la comunicació entre el codi Dart i el codi natiu (Java/Kotlin per a Android i Objective-C/Swift per a iOS). Aquesta funcionalitat és essencial quan necessites accedir a funcionalitats específiques del sistema operatiu que no estan disponibles directament a través de Flutter.
Objectius d'Aprenentatge
- Comprendre què són els canals de plataforma i per què són necessaris.
- Aprendre a configurar un canal de plataforma bàsic.
- Implementar la comunicació entre Flutter i el codi natiu.
- Veure exemples pràctics d'ús de canals de plataforma.
Què són els Canals de Plataforma?
Els canals de plataforma permeten enviar missatges entre el codi Dart i el codi natiu. Això es fa mitjançant un canal de missatges que pot ser utilitzat per invocar mètodes natius des de Flutter i viceversa.
Components Clau
- MethodChannel: Utilitzat per enviar missatges des de Flutter a la plataforma nativa.
- EventChannel: Utilitzat per rebre fluxos d'esdeveniments des de la plataforma nativa a Flutter.
- BasicMessageChannel: Utilitzat per enviar missatges bàsics en ambdues direccions.
Configuració d'un Canal de Plataforma Bàsic
Pas 1: Configuració del Codi Dart
Primer, configurem el codi Dart per enviar un missatge al codi natiu.
import 'package:flutter/services.dart'; class PlatformChannelExample { static const platform = MethodChannel('com.example.platform_channel'); Future<String> getNativeMessage() async { try { final String result = await platform.invokeMethod('getNativeMessage'); return result; } on PlatformException catch (e) { return "Error: '${e.message}'."; } } }
Pas 2: Configuració del Codi Natiu (Android)
A continuació, configurem el codi natiu per Android. Afegim el següent codi a MainActivity.kt
o MainActivity.java
.
Kotlin
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.platform_channel" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getNativeMessage") { val message = getNativeMessage() result.success(message) } else { result.notImplemented() } } } private fun getNativeMessage(): String { return "Hola des de Kotlin!" } }
Java
import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.MethodChannel; 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("getNativeMessage")) { String message = getNativeMessage(); result.success(message); } else { result.notImplemented(); } } ); } private String getNativeMessage() { return "Hola des de Java!"; } }
Pas 3: Configuració del Codi Natiu (iOS)
Finalment, configurem el codi natiu per iOS. Afegim el següent codi a AppDelegate.swift
o AppDelegate.m
.
Swift
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 channel = FlutterMethodChannel(name: "com.example.platform_channel", binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getNativeMessage" { result(self.getNativeMessage()) } else { result(FlutterMethodNotImplemented) } }) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } private func getNativeMessage() -> String { return "Hola des de Swift!" } }
Objective-C
#import "AppDelegate.h" #import "GeneratedPluginRegistrant.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"com.example.platform_channel" binaryMessenger:controller.binaryMessenger]; [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { if ([@"getNativeMessage" isEqualToString:call.method]) { result([self getNativeMessage]); } else { result(FlutterMethodNotImplemented); } }]; [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (NSString*)getNativeMessage { return @"Hola des de Objective-C!"; } @end
Exemple Pràctic
Ara que hem configurat el canal de plataforma, podem utilitzar-lo en una aplicació Flutter. Aquí teniu un exemple pràctic:
import 'package:flutter/material.dart'; import 'platform_channel_example.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Canals de Plataforma'), ), body: Center( child: FutureBuilder<String>( future: PlatformChannelExample().getNativeMessage(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { return Text('Missatge natiu: ${snapshot.data}'); } }, ), ), ), ); } }
Exercici Pràctic
Objectiu
Implementar un canal de plataforma que permeti obtenir la versió del sistema operatiu des del codi natiu.
Instruccions
- Configura un canal de plataforma en el codi Dart.
- Implementa el codi natiu per Android i iOS per retornar la versió del sistema operatiu.
- Mostra la versió del sistema operatiu en una interfície d'usuari de Flutter.
Solució
Codi Dart
import 'package:flutter/services.dart'; class PlatformVersion { static const platform = MethodChannel('com.example.platform_version'); Future<String> getPlatformVersion() async { try { final String version = await platform.invokeMethod('getPlatformVersion'); return version; } on PlatformException catch (e) { return "Error: '${e.message}'."; } } }
Codi Natiu (Android - Kotlin)
import android.os.Build 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.platform_version" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getPlatformVersion") { val version = "Android ${Build.VERSION.RELEASE}" result.success(version) } else { result.notImplemented() } } } }
Codi Natiu (iOS - Swift)
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 channel = FlutterMethodChannel(name: "com.example.platform_version", binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getPlatformVersion" { result("iOS " + UIDevice.current.systemVersion) } else { result(FlutterMethodNotImplemented) } }) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
Interfície d'Usuari de Flutter
import 'package:flutter/material.dart'; import 'platform_version.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Versió del Sistema Operatiu'), ), body: Center( child: FutureBuilder<String>( future: PlatformVersion().getPlatformVersion(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } else { return Text('Versió del sistema operatiu: ${snapshot.data}'); } }, ), ), ), ); } }
Conclusió
Els canals de plataforma són una eina essencial per accedir a funcionalitats específiques del sistema operatiu des de Flutter. Amb aquesta guia, has après a configurar un canal de plataforma bàsic i a implementar la comunicació entre Flutter i el codi natiu. Aquesta habilitat et permetrà crear aplicacions més riques i integrades amb les capacitats del dispositiu.
Curs de Desenvolupament Flutter
Mòdul 1: Introducció a Flutter
- Què és Flutter?
- Configuració de l'Entorn de Desenvolupament
- Comprensió de l'Arquitectura de Flutter
- Creació de la Teva Primera Aplicació Flutter
Mòdul 2: Conceptes Bàsics de Programació en Dart
- Introducció a Dart
- Variables i Tipus de Dades
- Sentències de Flux de Control
- Funcions i Mètodes
- Programació Orientada a Objectes en Dart
Mòdul 3: Widgets de Flutter
- Introducció als Widgets
- Widgets Stateless vs Stateful
- Widgets Bàsics
- Widgets de Disseny
- Widgets d'Entrada i Formulari
Mòdul 4: Gestió de l'Estat
Mòdul 5: Navegació i Enrutament
- Introducció a la Navegació
- Navegació Bàsica
- Rutes Nomenades
- Passar Dades Entre Pantalles
- Deep Linking
Mòdul 6: Xarxes i APIs
- Obtenir Dades d'Internet
- Analitzar Dades JSON
- Gestió d'Errors de Xarxa
- Ús d'APIs REST
- Integració de GraphQL
Mòdul 7: Persistència i Emmagatzematge
- Introducció a la Persistència
- Preferències Compartides
- Emmagatzematge de Fitxers
- Base de Dades SQLite
- Ús de Hive per a l'Emmagatzematge Local
Mòdul 8: Conceptes Avançats de Flutter
- Animacions en Flutter
- Pintura Personalitzada i Canvas
- Canals de Plataforma
- Isolates i Concurrència
- Optimització del Rendiment
Mòdul 9: Proves i Depuració
- Introducció a les Proves
- Proves Unitàries
- Proves de Widgets
- Proves d'Integració
- Tècniques de Depuració
Mòdul 10: Desplegament i Manteniment
- Preparació per al Llançament
- Construcció per a iOS
- Construcció per a Android
- Integració i Desplegament Continu (CI/CD)
- Manteniment i Actualització de la Teva Aplicació