claudneysessa / flutter_platform_channel

Exemplo de Utilização das Várias Formas de Comunicação do Flutter com o Código Nativo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

flutter_platform_channel

Exemplo de Utilização de Canal de Comunicação entre Flutter e Nativo (Android)

Project Flutter Version

    Flutter 3.7.5 • channel stable • https://github.com/flutter/flutter.git
    Framework • revision c07f788888 (11 months ago) • 2023-02-22 17:52:33 -0600
    Engine • revision 0f359063c4
    Tools • Dart 2.19.2 • DevTools 2.20.1

Sobre

Este projeto tem como objetivo demonstrar a utilização de um canal de comunicação entre o Flutter e o Android Nativo.

Em seu desenvolvimento foram feitas as seguintes implementações:

  • Chamada ao MethodChannel para execução de uma função nativa
  • Chamada ao MethodChannel para execução de uma função nativa passando parâmetros
  • Chamada ao EventChannel para execução de uma função nativa que retorna um Stream
  • Chamada ao BasicMessageChannel para execução de uma função nativa que retorna um Stream semelhante a uma função com retorno do tipo Future

Platform Channel

O Platform Channel é um recurso do Flutter que permite a comunicação entre o código Dart e o código nativo (Android, iOS).

O Platform Channel é composto por 3 tipos de canais de comunicação:

  • MethodChannel
  • EventChannel
  • BasicMessageChannel

Abaixo segue a descrição de cada um dos canais de comunicação utilizados neste projeto.

MethodChannel

O MethodChannel é um canal de comunicação que permite a chamada de funções nativas a partir do Flutter.

No exemplo de utilização do MethodChannel, foi criado um serviço com uma função que retorna uma string podendo esta ser concatenada ou não a uma string informada via argumento para o retorno. Caso seja informando um argumento para a função nativa esta irá retornar a string concatenada com o nome informado.

Service MethodChannel

import 'package:flutter/services.dart';

class ExemploMethodChannelService {
  var platform = const MethodChannel('br.com.claudneysessa/ExemploMethodChannel');

  ExemploMethodChannelService() {
    platform.setMethodCallHandler(_callbackPlatformChannel);
  }

  Future<String> callSimpleMethodChannel() async => await platform.invokeMethod('getHelloWorld');

  Future<String> callSimpleMethodChannelWithParams(String param) async {
    if (param.isEmpty) return '';
    return await platform.invokeMethod('getHelloWorld', {'message': param});
  }

  Future<void> _callbackPlatformChannel(MethodCall call) async {
    final String args = call.arguments;
    switch (call.method) {
      case 'methodCallback':
        print('Method Channel Callback -> $args');
    }
  }
}

No exemplo acima, foi criado um serviço que utiliza o MethodChannel para chamar uma função nativa que retorna uma string.

var platform = const MethodChannel('br.com.claudneysessa/ExemploMethodChannel');

Platform MethodChannel

Este é o canal de comunicação que será utilizado para chamar a função nativa neste caso o java.

public class ExemploMethodChannel {

  private static final String CHANNEL = "br.com.claudneysessa/ExemploMethodChannel";

  public ExemploMethodChannel(Context context, FlutterEngine flutterEngine) {
    new MethodChannel(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL
    )
      .setMethodCallHandler(
        new MethodCallHandler() {
          @Override
          public void onMethodCall(MethodCall call, Result result) {
            if ("getHelloWorld".equals(call.method)) {
              String message = call.argument("message");
              if (message == null) {
                result.success("Exemplo de MethodChannel");
              } else {
                result.success("Exemplo de MethodChannel, Texto Concatenado -> " + message);
                new MethodChannel(
                  flutterEngine.getDartExecutor().getBinaryMessenger(),
                  CHANNEL
                )
                  .invokeMethod("methodCallback", "result callback kt");
              }
            } else {
              result.notImplemented();
            }
          }
        }
      );
  }
}

EventChannel

O EventChannel é um canal de comunicação que permite a chamada de funções nativas que retornam um Stream.

No exemplo de utilização do EventChannel, foi criado um serviço que retorna uma string concatenando um nome para o evento e a data e hora atual.

Platform Channel (Java Nativo)

public class ExemploEventChannel {

  private static final String CHANNEL_Method = "br.com.claudneysessa/ExemploEventChannelMethos";
  private static final String CHANNEL_Event = "br.com.claudneysessa/ExemploEventChannelEvents";

  private EventSink eventSink;
  private Handler handler;
  private FlutterEngine flutterEngine;

  String nomeEvento = "";
  int tipoEvento = 0;

  public ExemploEventChannel(Context context, FlutterEngine flutterEngine) {

    System.out.println("ExemploEventChannel -> Criando o Channel");

    this.flutterEngine = flutterEngine;

    setupEventChannel();

  }

  private void setupEventChannel() {

    System.out.println("ExemploEventChannel -> Configurando o MethodChannel");

    new MethodChannel(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL_Method
    )
      .setMethodCallHandler(

        new MethodCallHandler() {

          @Override
          public void onMethodCall(MethodCall call, Result result) {
            switch (call.method) {


              //
              // Altera o Tipo do Evento
              //

              case "alterarEvento":

                alterarEvento();

                break;

              //
              // Altera o Nome do Evento e o Tipo do Evento
              //

              case "alterarEventoNome":

                HashMap map = call.argument("args");
                String nomeParametro = (String) map.get("nomeEvento");

                alterarEventoNome(nomeParametro);

                break;

            }

          }

        }

      );

    System.out.println("ExemploEventChannel -> Configurando o EventChannel");

    EventChannel eventChannel = new EventChannel(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL_Event
    );

    System.out.println("ExemploEventChannel -> Configurando o CustomStreamHandler");

    eventChannel.setStreamHandler(new CustomStreamHandler());
  }

  public void alterarEvento() {

    System.out.println("ExemploEventChannel -> Alterando o Tipo do Evento");

    if (tipoEvento == 0) {
      tipoEvento = 1;
    } else {
      tipoEvento = 0;
    }

  }

  public void alterarEventoNome(String value) {

    System.out.println("ExemploEventChannel -> Alterando o Nome e o Tipo do Evento");

    nomeEvento = value;

    if (tipoEvento == 0) {
      tipoEvento = 1;
    } else {
      tipoEvento = 0;
    }
  }

  private void cancelEventChannel() {

    System.out.println("ExemploEventChannel -> Cancelando o EventChannel");

    if (eventSink != null) {
      eventSink.endOfStream();
      eventSink = null;
    }

    if (handler != null) {
      handler.removeCallbacksAndMessages(null);
      handler = null;
    }

  }

  private class CustomStreamHandler implements StreamHandler {

    @Override
    public void onListen(Object arguments, EventSink events) {

      eventSink = events;
      handler = new Handler(Looper.getMainLooper());

      System.out.println("ExemploEventChannel -> Iniciando o CustomStreamHandler");

      handler.postDelayed(
        new Runnable() {
          @Override
          public void run() {

            if (eventSink != null) {
              String retornoFuncao = "";

              SimpleDateFormat sdf = new SimpleDateFormat("dd/M/yyyy hh:mm:ss");

              if (tipoEvento == 0) {
                retornoFuncao = nomeEvento + " 1 - " + sdf.format(new Date());
              } else {
                retornoFuncao = nomeEvento + " 2 - " + sdf.format(new Date());
              }

              eventSink.success(retornoFuncao);

              handler.postDelayed(this, 1000);
            }
          }
        },
        0
      );
    }

    @Override
    public void onCancel(Object arguments) {

      System.out.println("ExemploEventChannel -> Solicitando o cancelamento do EventChannel");

      cancelEventChannel();

    }
  }
}

Além do MethodChannel Foram adicionados 2 eventos para alterar o tipo de retorno do Stream, sendo eles:

  • alterarEvento: Altera o tipo do evento entre 1 e 2
  • alterarEventoNome 2: Altera o nome do evento para e o tipo do evento entre 1 e 2
    new MethodChannel(
        flutterEngine.getDartExecutor().getBinaryMessenger(),
        CHANNEL_Method
    )
        .setMethodCallHandler(

        new MethodCallHandler() {

            @Override
            public void onMethodCall(MethodCall call, Result result) {
            switch (call.method) {


                //
                // Altera o Tipo do Evento
                //

                case "alterarEvento":

                alterarEvento();

                break;

                //
                // Altera o Nome do Evento e o Tipo do Evento
                //

                case "alterarEventoNome":

                HashMap map = call.argument("args");
                String nomeParametro = (String) map.get("nomeEvento");

                alterarEventoNome(nomeParametro);

                break;

            }

            }

        }

        );

    public void alterarEvento() {

        System.out.println("ExemploEventChannel -> Alterando o Tipo do Evento");

        if (tipoEvento == 0) {
            tipoEvento = 1;
        } else {
            tipoEvento = 0;
        }

    }

    public void alterarEventoNome(String value) {

        System.out.println("ExemploEventChannel -> Alterando o Nome e o Tipo do Evento");

        nomeEvento = value;

        if (tipoEvento == 0) {
            tipoEvento = 1;
        } else {
            tipoEvento = 0;
        }
    }

Estes eventos foram criados em um MethodChannel separado que é chamado a partir do Flutter.

private static final String CHANNEL_Method = "br.com.claudneysessa/ExemploEventChannelMethos";

Service MethodChannel (Dart/Flutter)

Service no Flutter que chama o MethodChannel que invoca as funções para alterar o tipo de evento e o nome do evento.

import 'package:flutter/services.dart';

class ExemploEventChannelService {
  late MethodChannel _platformChannel;

  ExemploEventChannelService() {
    _platformChannel = const MethodChannel('br.com.claudneysessa/ExemploEventChannelMethos');
  }

  Future<void> alterarEvento() async {
    _platformChannel.invokeMethod('alterarEvento');
  }

  Future<void> alterarEventoNome({
    String nomeEvento = '',
  }) async {
    _platformChannel.invokeMethod(
      'alterarEventoNome',
      {
        'args': {
          'nomeEvento': nomeEvento,
        }
      },
    );
  }
}

Platform EventChannel (Java Nativo)

Também foi criado um EventChannel que retorna um Stream com o nome do evento e a data e hora atual.

    private void setupEventChannel() {

    EventChannel eventChannel = new EventChannel(
        flutterEngine.getDartExecutor().getBinaryMessenger(),
        CHANNEL_Event
    );

    eventChannel.setStreamHandler(new CustomStreamHandler());
    }

    private class CustomStreamHandler implements StreamHandler {

        @Override
        public void onListen(Object arguments, EventSink events) {

            eventSink = events;
            handler = new Handler(Looper.getMainLooper());

            System.out.println("ExemploEventChannel -> Iniciando o CustomStreamHandler");

            handler.postDelayed(
            new Runnable() {
                @Override
                public void run() {

                if (eventSink != null) {
                    String retornoFuncao = "";

                    SimpleDateFormat sdf = new SimpleDateFormat("dd/M/yyyy hh:mm:ss");

                    if (tipoEvento == 0) {
                    retornoFuncao = nomeEvento + " 1 - " + sdf.format(new Date());
                    } else {
                    retornoFuncao = nomeEvento + " 2 - " + sdf.format(new Date());
                    }

                    eventSink.success(retornoFuncao);

                    handler.postDelayed(this, 1000);
                }
                }
            },
            0
            );
        }

        @Override
        public void onCancel(Object arguments) {
            cancelEventChannel();
        }
    }

Service EventChannel (Dart/Flutter)

Service no Flutter que chama o EventChannel que disponibiliza o Stream.

import 'package:flutter/services.dart';

class ExemploEventChannelService {
  late EventChannel _eventStream;

  ExemploEventChannelService() {
    _eventStream = const EventChannel('br.com.claudneysessa/ExemploEventChannelEvents');
  }

  Stream<dynamic> receiveBroadcastStream() {
    return _eventStream.receiveBroadcastStream();
  }

}

Service MethodChannel/EventChannel Completo

Como ambos os canais de comunicação estão no mesmo serviço, foi criado um serviço que chama o EventChannel que disponibiliza o Stream e também chama o MethodChannel que invoca as funções para alterar o tipo de evento e o nome do evento.

Java Nativo

public class ExemploEventChannel {

  private static final String CHANNEL_Method = "br.com.claudneysessa/ExemploEventChannelMethos";
  private static final String CHANNEL_Event = "br.com.claudneysessa/ExemploEventChannelEvents";

  private EventSink eventSink;
  private Handler handler;
  private FlutterEngine flutterEngine;

  String nomeEvento = "";
  int tipoEvento = 0;

  public ExemploEventChannel(Context context, FlutterEngine flutterEngine) {

    System.out.println("ExemploEventChannel -> Criando o Channel");

    this.flutterEngine = flutterEngine;

    setupEventChannel();

  }

  private void setupEventChannel() {

    System.out.println("ExemploEventChannel -> Configurando o MethodChannel");

    new MethodChannel(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL_Method
    )
      .setMethodCallHandler(

        new MethodCallHandler() {

          @Override
          public void onMethodCall(MethodCall call, Result result) {
            switch (call.method) {


              //
              // Altera o Tipo do Evento
              //

              case "alterarEvento":

                alterarEvento();

                break;

              //
              // Altera o Nome do Evento e o Tipo do Evento
              //

              case "alterarEventoNome":

                HashMap map = call.argument("args");
                String nomeParametro = (String) map.get("nomeEvento");

                alterarEventoNome(nomeParametro);

                break;

            }

          }

        }

      );

    System.out.println("ExemploEventChannel -> Configurando o EventChannel");

    EventChannel eventChannel = new EventChannel(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL_Event
    );

    System.out.println("ExemploEventChannel -> Configurando o CustomStreamHandler");

    eventChannel.setStreamHandler(new CustomStreamHandler());
  }

  public void alterarEvento() {

    System.out.println("ExemploEventChannel -> Alterando o Tipo do Evento");

    if (tipoEvento == 0) {
      tipoEvento = 1;
    } else {
      tipoEvento = 0;
    }

  }

  public void alterarEventoNome(String value) {

    System.out.println("ExemploEventChannel -> Alterando o Nome e o Tipo do Evento");

    nomeEvento = value;

    if (tipoEvento == 0) {
      tipoEvento = 1;
    } else {
      tipoEvento = 0;
    }
  }

  private void cancelEventChannel() {

    System.out.println("ExemploEventChannel -> Cancelando o EventChannel");

    if (eventSink != null) {
      eventSink.endOfStream();
      eventSink = null;
    }

    if (handler != null) {
      handler.removeCallbacksAndMessages(null);
      handler = null;
    }

  }

  private class CustomStreamHandler implements StreamHandler {

    @Override
    public void onListen(Object arguments, EventSink events) {

      eventSink = events;
      handler = new Handler(Looper.getMainLooper());

      System.out.println("ExemploEventChannel -> Iniciando o CustomStreamHandler");

      handler.postDelayed(
        new Runnable() {
          @Override
          public void run() {

            if (eventSink != null) {
              String retornoFuncao = "";

              SimpleDateFormat sdf = new SimpleDateFormat("dd/M/yyyy hh:mm:ss");

              if (tipoEvento == 0) {
                retornoFuncao = nomeEvento + " 1 - " + sdf.format(new Date());
              } else {
                retornoFuncao = nomeEvento + " 2 - " + sdf.format(new Date());
              }

              eventSink.success(retornoFuncao);

              handler.postDelayed(this, 1000);
            }
          }
        },
        0
      );
    }

    @Override
    public void onCancel(Object arguments) {

      System.out.println("ExemploEventChannel -> Solicitando o cancelamento do EventChannel");

      cancelEventChannel();

    }
  }
}

Dart/Flutter

import 'package:flutter/services.dart';

class ExemploEventChannelService {
  late EventChannel _eventStream;
  late MethodChannel _platformChannel;

  ExemploEventChannelService() {
    _eventStream = const EventChannel('br.com.claudneysessa/ExemploEventChannelEvents');
    _platformChannel = const MethodChannel('br.com.claudneysessa/ExemploEventChannelMethos');
  }

  Stream<dynamic> receiveBroadcastStream() {
    return _eventStream.receiveBroadcastStream();
  }

  Future<void> alterarEvento() async {
    _platformChannel.invokeMethod('alterarEvento');
  }

  Future<void> alterarEventoNome({
    String nomeEvento = '',
  }) async {
    _platformChannel.invokeMethod(
      'alterarEventoNome',
      {
        'args': {
          'nomeEvento': nomeEvento,
        }
      },
    );
  }
}

O objetivo deste exemplo foi executar o serviço nativo que fica em execução e alterar o tipo de retorno do Stream mantendo a conexão com o serviço.

BasicMessageChannel

O BasicMessageChannel é um canal de comunicação que permite a chamada de funções nativas que retornam um Stream semelhante a uma função com retorno do tipo Future.

Java Nativo

public class ExemploBasicMessageChannel {

  private static final String CHANNEL =
    "br.com.claudneysessa/ExemploBasicMessageChannel";

  private Handler handler;

  public ExemploBasicMessageChannel(
    Context context,
    FlutterEngine flutterEngine
  ) {

    CustomLogger logger = new CustomLogger();

    logger.info("Criando o Channel");

    ExemploBasicMessageChannel outerInstance = this;

    new BasicMessageChannel<>(
      flutterEngine.getDartExecutor().getBinaryMessenger(),
      CHANNEL,
      StandardMessageCodec.INSTANCE
    )
      .setMessageHandler(

        new BasicMessageChannel.MessageHandler<Object>() {
          @Override
          public void onMessage(
            Object message,
            BasicMessageChannel.Reply<Object> reply
          ) {

            logger.info("Criando o Channel");
            logger.error("Criando o Channel");
            logger.warning("Criando o Channel");
            logger.alert("Criando o Channel");
            logger.log("Criando o Channel");
            logger.printCustomMsgBox("Criando o Channel", "info", CHANNEL);

            HashMap map = (HashMap) message;

            System.out.println(message);

            logger.info("Funcao: " + map.get("funcaoNome"));

            String funcaoNome = (String) map.get("funcaoNome");
            String funcaoParametro = (String) map.get("funcaoParametro");

            switch (funcaoNome) {
              case "obterImagemAndroidAssets":

                logger.info("Executando funcao obterImagemAndroidAssets");

                try {
                  outerInstance.handler = new Handler(Looper.getMainLooper());

                  InputStream inputStream = context
                    .getAssets()
                    .open(funcaoParametro);

                  byte[] imageBytes = new byte[inputStream.available()];

                  inputStream.read(imageBytes);

                  outerInstance.handler.postDelayed(
                    () -> {

                      logger.info("Enviando imagem para o Flutter");

                      reply.reply(imageBytes);

                    },
                    10000
                  );

                } catch (IOException e) {

                  logger.error("Erro ao obter imagem");

                  e.printStackTrace();

                  reply.reply(null);

                }
                break;
            }

          }
        }
      );
  }
}

Dart/Flutter

Service no Flutter que chama o BasicMessageChannel que invoca a função para obter a imagem da pasta assets do android.

import 'package:flutter/services.dart';

class ExemploBasicMessageChannelService {
  static const _basicMessageChannel = BasicMessageChannel<dynamic>(
    'br.com.claudneysessa/ExemploBasicMessageChannel',
    StandardMessageCodec(),
  );

  static Future<Uint8List> getImage() async {
    final reply = await _basicMessageChannel.send(
      {
        'funcaoNome': 'obterImagemAndroidAssets',
        'funcaoParametro': 'flutter2.jpg',
      },
    ) as Uint8List?;

    if (reply == null) {
      throw PlatformException(
        code: 'Error',
        message: 'Falha ao buscar imagem no Android',
        details: null,
      );
    }

    return reply;
  }
}

No exemplo de utilização do BasicMessageChannel, foi criado um serviço que retorna uma imagem da pasta assets do android e tudo isto após aguardar 10 segundos programados na função.

Autor

Projeto desenvolvido por Claudney Sarti Sessa

Claudney Sarti Sessa GitHub https://github.com/claudneysessa
GitHub Pages https://claudneysessa.github.io
Instagram https://www.instagram.com/claudneysessa
LinkedIn https://www.linkedin.com/in/claudneysessa
Gmail claudneysartisessa@gmail.com

Licença

Este projeto está licenciado sob a Licença MIT - veja o arquivo LICENSE para mais detalhes.

About

Exemplo de Utilização das Várias Formas de Comunicação do Flutter com o Código Nativo


Languages

Language:Dart 75.3%Language:Java 24.7%