Conectando seu aplicativo Flutter a um back-end

Tempo de leitura: 5 minutes

Ao desenvolver um aplicativo Flutter, você provavelmente deseja manter os custos de desenvolvimento em um nível mínimo, tornar a solução rápida, confiável e segura. Neste guia (ou melhor, uma visão geral técnica com comentários), explicarei como conectar seu aplicativo Flutter a um backend usando a API HTTP REST e o gRPC. Você aprenderá como escolher o backend certo para seu aplicativo, como configurá-lo e como conectar seu aplicativo a ele.

 

O que escolher?

Não é segredo que a API REST é a forma mais comum de comunicação entre o frontend e o backend em aplicativos da Web modernos e infraestruturas baseadas em microsserviços. No entanto, vale a pena observar que os microsserviços também podem usar outros métodos de comunicação.

A API HTTP REST usa o protocolo HTTP para transferir dados entre o cliente e o servidor. Ela é fácil de usar e compreensível para a maioria dos desenvolvedores. Para usá-la, você precisa definir as solicitações que seu aplicativo deve enviar ao servidor, bem como a estrutura das respostas que você espera receber. Este artigo fornece um exemplo usando o pacote Dio.

O gRPC (Google Remote Procedure Call) é uma abordagem mais recente para a comunicação entre o cliente e o servidor, com base na arquitetura RPC de código aberto. Ele usa o formato de troca de mensagens Protobuf, que é um formato altamente eficiente para a troca de mensagens com um alto grau de empacotamento (graças ao uso forçado dos recursos http/2) para serializar dados estruturados. Em alguns casos, o uso da API gRPC pode ser mais eficiente do que a API REST.

Ao desenvolver um aplicativo, o frontend e o backend geralmente são criados por pessoas diferentes com competências diferentes. Ferramentas como swagger e redoc são usadas para fornecer instruções de uso para REST. No caso do gRPC, o frontend recebe um código quase pronto para ser implementado. Também vale a pena considerar o fato de que, se o projeto envolve um aplicativo da Web, usar o gRPC para um aplicativo móvel pode ser muito caro.

Conexão com HTTP REST

Para trabalhar com a API REST, recomendo usar o pacote Dio porque ele é mais funcional e conveniente do que o pacote http padrão. Se você já criou aplicativos da Web, ele é semelhante ao axios.

Para usar o Dio, é necessário criar uma classe para trabalhar com conexões de rede, na qual você definirá as solicitações que o aplicativo deve enviar ao servidor, bem como a estrutura das respostas que espera receber.

flutter pub add dio

Adicione o pacote dio ao seu projeto

Vamos criar uma classe para trabalhar com conexões de rede:

import 'package:dio/dio.dart';

class NetworkService {
  late final Dio _dio;
  final JsonEncoder _encoder = const JsonEncoder();
  static final NetworkService _instance = NetworkService.internal();

  NetworkService.internal();

  static NetworkService get instance => _instance;

  Future<void> initClient() async {
    _dio = Dio(
      BaseOptions(
        baseUrl: Constant.baseUrl,
        connectTimeout: 60000,
        receiveTimeout: 6000,
      ),
    );
// A place for interceptors. For example, for authentication and logging
  }

  Future<dynamic> get(
    String url, {
    Map<String, dynamic>? queryParameters,
  }) async {
    try {
      final response = await _dio.get(url, queryParameters: queryParameters);
      return response.data;
    } on DioError catch (e) {
      final data = Map<String, dynamic>.from(e.response?.data);
      throw Exception(data['message'] ?? "Error while fetching data");
    } catch (e) {
      rethrow;
    }
  }

  Future<dynamic> download(String url, String path) async {
    return _dio.download(url, path).then((Response response) {
      if (response.statusCode! < 200 || response.statusCode! > 400) {
        throw Exception("Error while fetching data");
      }
      return response.data;
    }).onError((error, stackTrace) {
      throw Exception(error);
    });
  }

  Future<dynamic> delete(String url) async {
    return _dio.delete(url).then((Response response) {
      if (response.statusCode! < 200 || response.statusCode! > 400) {
        throw Exception("Error while fetching data");
      }
      return response.data;
    }).onError((DioError error, stackTrace) {
      _log(error.response);
    });
  }

  Future<dynamic> post(String url, {body, encoding}) async {
    try {
      final response = await _dio.post(url, data: _encoder.convert(body));
      return response.data;
    } on DioError catch (e) {
      throw Exception(e.response?.data['detail'] ?? e.toString());
    } catch (e) {
      rethrow;
    }
  }

  Future<dynamic> postFormData(String url, {required FormData data}) async {
    try {
      final response = await _dio.post(url, data: data);
      return response.data;
    } on DioError catch (e) {
      throw Exception(e.response?.data['detail'] ?? e.toString());
    } catch (e) {
      rethrow;
    }
  }

  Future<dynamic> patch(String url, {body, encoding}) async {
    try {
      final response = await _dio.patch(url, data: _encoder.convert(body));
      return response.data;
    } on DioError catch (e) {
      throw Exception(e.response?.data['detail'] ?? e.toString());
    } catch (e) {
      rethrow;
    }
  }

  Future<dynamic> put(String url, {body, encoding}) async {
    try {
      final response = await _dio.put(url, data: _encoder.convert(body));
      return response.data;
    } on DioError catch (e) {
      throw e.toString();
    } catch (e) {
      rethrow;
    }
  }
}
Exemplo

final NetworkService _client;

Future<String> login(LoginRequest loginRequest) async {
  try {
    final jsonData = await _client.post(
      "${Constant.baseUrl}/v1/auth/login",
      body: loginRequest.toJson()
    );
    return jsonData['access_token']
  } catch (e) {
    rethrow;
  }
}

 

Conexão com o gRPC

Para trabalhar com o gRPC, você precisa usar o código gerado com base nos arquivos proto. Crie um serviço para o trabalho, que usará a classe HelloClient gerada usando o gRPC. Ao criar uma instância do HelloClient, você precisará passar a ele um canal que será usado para enviar solicitações.

Agora, integre o cliente gRPC gerado em seu aplicativo Flutter:

flutter pub add grpc

Adicione o pacote grpc ao seu projeto

Criar um serviço para o trabalho:

import 'package:grpc/grpc.dart';
//importar seu código de geração automática
import '../services/proto/hello.pbgrpc.dart';

class HelloService {

  /// Aqui, digite seu host sem a parte http
  String baseUrl = "example.com";

  HelloService._internal();
  static final HelloService _instance = HelloService._internal();

  factory HelloService() => _instance;

  ///instância estática do HelloService que chamaremos quando quisermos fazer solicitações
  static HelloService get instance => _instance;
  ///HelloClient é a classe que foi gerada para nós quando executamos o comando de geração
  ///Passaremos um canal a ele para inicializá-lo
  late HelloClient _helloClient;

  ///será usado para criar um canal quando criarmos essa classe.
  ///Chame HelloService().init() antes de fazer qualquer chamada.
  Future<void> init() async {
    final channel = ClientChannel(
      baseUrl,
      port: 443,
      options: const ChannelOptions(),
    );
    _helloClient = HelloClient(channel);
  }

  ///fornecer acesso público à instância do HelloClient
  HelloClient get helloClient {
    return _helloClient;
  }
}
Em sua função principal, inicialize a classe HelloService da seguinte forma.

HelloService().init();

Fazer chamada gRPC

Future<void> sayHello() async {
  try {
    HelloRequest helloRequest = HelloRequest();
    helloRequest.name = "Itachi";
    var res = await HelloService.instance.helloClient.sayHello(helloRequest);
  } catch (e) {
    print(e);
  }
}

 

Comparação entre REST e gRPC para aplicativos Flutter

  1. Vantagens do REST: Simples, fácil de entender, amplamente suportado, armazenável em cache e adequado para a maioria dos aplicativos.
  2. Desvantagens do REST: Maior latência, serialização menos eficiente, recursos de streaming limitados.
  3. Vantagens do gRPC: Baixa latência, serialização eficiente, suporte a streaming, bibliotecas de clientes robustas e ideal para aplicativos em tempo real e microsserviços.
  4. Desvantagens do gRPC: curva de aprendizado mais acentuada, não é tão legível para humanos, tem menos suporte em navegadores da Web e pode ser um exagero para aplicativos simples.

Ao escolher entre REST e gRPC para seu aplicativo Flutter, considere as necessidades e os requisitos específicos do seu aplicativo. O REST é uma escolha sólida para a maioria dos aplicativos, enquanto o gRPC é adequado para aplicativos em tempo real ou para aqueles com padrões de comunicação complexos entre serviços.

 

Neste artigo, exploramos como organizar um aplicativo Flutter com um backend de API, com foco em duas abordagens principais: HTTP REST e gRPC. Discutimos o projeto de APIs e a implementação de clientes no Flutter.