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

  1. Configura un canal de plataforma en el codi Dart.
  2. Implementa el codi natiu per Android i iOS per retornar la versió del sistema operatiu.
  3. 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

Mòdul 2: Conceptes Bàsics de Programació en Dart

Mòdul 3: Widgets de Flutter

Mòdul 4: Gestió de l'Estat

Mòdul 5: Navegació i Enrutament

Mòdul 6: Xarxes i APIs

Mòdul 7: Persistència i Emmagatzematge

Mòdul 8: Conceptes Avançats de Flutter

Mòdul 9: Proves i Depuració

Mòdul 10: Desplegament i Manteniment

Mòdul 11: Flutter per a Web i Escriptori

© Copyright 2024. Tots els drets reservats