Executando verificação de segurança...
1

Dart Puro - Como consumir uma API - Modelando dados ( fromJson/fromMap )

Na aula anterior, nós consumimos a API ViaCep, se você ainda não viu, aproveite para dar uma olhada antes de continuar aqui:
Dart Puro - Como consumir uma API - Primeira Requesição

Agora que você já leu o post anterior, vamos prosseguir...

O que é Modelagem de Dados

É literalmente você pegar um conjuto de dados que estão em um formato especifico (No nosso caso é o Json ) e transforma-lo em um formato mais amigável para linguagem que estamos utilizando ( Dart no nosso caso ).

Criação da Model

A model nada mais é que uma classe com um nome/termo conhecido de forma geral pela galera da tecnologia. Veja como vamos fazer:

import 'package:http/http.dart' as http;

class ViaCepModel {
  
 final String cep;
 final String logradouro;
 final String bairro;
 final String localidade;
 final String uf;
  
 ViaCepModel({
   required this.cep,
   required this.logradouro,
   required this.bairro,
   required this.localidade,
   required this.uf,
 });
  
}

Future<void> main() async {
  final url = Uri.parse('https://viacep.com.br/ws/01001000/json/');

  final response = await http.get(url);

  if (response.statusCode == 200) {
    print(response.body);
  }
}

Veja que criamos a classe e colocamos um nome bem legível 'ViaCepModel' e colocamos os atributos que queremos usar, note que eu não preciso estudar a API para saber o que ela retornar para mim, eu consigo fazer isso olhando para o model.

ATENÇÃO

O uso dos models é muito importante, se você não reconhece isso, veja o vídeo que eu fiz sobre o assunto: O que é um Model?

Nesse vídeo acima eu explico detalhadamente coisas que usaremos aqui, fará mais sentido se você assistir o vídeo.

Utilizando o nosso Model

Agora já temos o Model pronto para ser utilizado, porém ainda não estamos utilizando ele, vamos fazer isso agora:

import 'package:http/http.dart' as http;
import 'dart:convert';

class ViaCepModel {
  
 final String cep;
 final String logradouro;
 final String bairro;
 final String localidade;
 final String uf;
  
 ViaCepModel({
   required this.cep,
   required this.logradouro,
   required this.bairro,
   required this.localidade,
   required this.uf,
 });
  
}

Future<void> main() async {
  final url = Uri.parse('https://viacep.com.br/ws/01001000/json/');

  final response = await http.get(url);

  ViaCepModel? cep;
  if (response.statusCode == 200) {
    final map = json.decode(response.body);
    
    cep = ViaCepModel(
      cep: map["cep"],
      logradouro: map['logradouro'],
      bairro: map['bairro'],
      localidade: map['localidade'],
      uf: map['uf'],
    );
    
  }
  
  print("CEP: $cep");
}

Muita coisa?
Na verdade não, é só entendermos o que foi adicionado...

Precisei colocar uma importação ('dart:convert') a mais para usar o 'json.decode', como o HTTP retorna o JSON para nós e o Dart 'não sabe o que é um JSON', nós precisamos decodificar para que o dado seja convertido para um tipo reconhecido pelo Dart, que no caso é o Map.

O 'response.body' é entendido como uma 'String' mas nós sabemos que ele tem cara de um 'Map', então nós precisamos mostrar para o Dart isso, no caso eu criei a variavél 'map' e coloquei o 'response.body' decodificado, logo nossa variável 'map' passou a ser 'Map'.

Logo abaixo, atribuimos os dados a nossa variável 'cep' que foi declarada acima do 'if' com a seguinte assinatura: 'ViaCepModel? cep;', ou seja, o tipo dela é a classe que criamos e também colocamos a possibilidade dela ser nula, se quiser saber mais sobre o assunto veja: Null-Safety na Linguagem Dart

Colocamos os atributos conforme o que os dados que estão na 'map'.

No final de tudo imprimos nossa classe na tela, ser clicarmos no 'Run', o resultado será:

CEP: Instance of 'ViaCepModel'

Legal, porém, queremos ver as informações, vamos fazer o seguinte,
no final da classe 'ViaCepModel' adicione o seguinte código:

  @override
  String toString() {
    return "ViaCepModel(cep: $cep, logradouro: $logradouro, bairro: $bairro, localidade: $localidade, uf: $uf)";
  }

Com isso podemos mudar aquele 'Instance of 'ViaCepModel'' para algo mais bonito, agora a resposta deve ser algo como:

CEP: ViaCepModel(cep: 01001-000, logradouro: Praça da Sé, bairro: Sé, localidade: São Paulo, uf: SP)

Mais bonito não é mesmo ?
Mas podemos deixar ainda melhor...

Utilizando o fromMap

O 'fromMap' é um termo para dizermos que queremos criar uma 'ViaCepModel' a partir de um 'Map'.
Eu posso criar como um método estático dentro da minha classe ou como um construtor de fábrica, no nosso caso usaremos o construtor de fábrica, funcioná da seguinte forma:

Adicione o seguinte código na sua classe 'ViaCepModel'

 factory ViaCepModel.fromMap(Map<String, dynamic> map) {
    return ViaCepModel(
      cep: map["cep"],
      logradouro: map['logradouro'],
      bairro: map['bairro'],
      localidade: map['localidade'],
      uf: map['uf'],
    );
  }

A classe deve se parecer com isso:

class ViaCepModel {
  
 final String cep;
 final String logradouro;
 final String bairro;
 final String localidade;
 final String uf;
  
 ViaCepModel({
   required this.cep,
   required this.logradouro,
   required this.bairro,
   required this.localidade,
   required this.uf,
 });
  
  
  factory ViaCepModel.fromMap(Map<String, dynamic> map) {
    return ViaCepModel(
      cep: map["cep"],
      logradouro: map['logradouro'],
      bairro: map['bairro'],
      localidade: map['localidade'],
      uf: map['uf'],
    );
  }
  
  
  @override
  String toString() {
    return "ViaCepModel(cep: $cep, logradouro: $logradouro, bairro: $bairro, localidade: $localidade, uf: $uf)";
  }
  
}

Também mudaremos a atribuição da variável 'cep' para isso:

cep = ViaCepModel.fromMap(map);

Fazendo com que a 'main' fique assim:

Future<void> main() async {
  final url = Uri.parse('https://viacep.com.br/ws/01001000/json/');

  final response = await http.get(url);

  ViaCepModel? cep;
  if (response.statusCode == 200) {
    final map = json.decode(response.body);
    
    cep = ViaCepModel.fromMap(map);
    
  }
  
  print("CEP: $cep");
}

Que interessante não é mesmo?
Não precisamos instanciar a classe 'ViaCepModel' e colocar manualmente os valores pelo contrutor, pois nós criamos um contrutor personalizado que fica responsável por pegar os valores e colocar nos atributos corretamente.

No caso nós podemos chamar o 'ViaCepModel.fromMap' e dentro dele passar o 'Map', porém note que ainda precisamos fazer a decodificação do 'response.body' para depois passarmos dentro do 'ViaCepModel.fromMap' e se nós não precisarmos fazer mais isso ? Parece Bom ?

Vamos fazer ué.

Utilizando o fromJson

É a mesma coisa que o fromMap, porém como o nome diz ele fará uma 'ViaCepModel' a parti de um 'Json', então vamos ao código.

Na sua classe adicione o seguinte código:

factory ViaCepModel.fromJson(String data) {
    return ViaCepModel.fromMap(json.decode(data));
}

Bem parecido com o 'fromMap' até né?
A única mudança é que agora pedimos o 'Json' e fazemos a decodificação dele dentro do método já e logo em seguida passamos o resultado esperado 'Map' para dento do 'ViaCepModel.fromMap' facilitando ainda mais as coisas.

Outra coisa que mudamos na 'main' é que agora não precisamos mais usar o 'json.decode' já que ele está dentro do 'ViaCepModel.fromJson', veja como a 'main' deve ficar:

Future<void> main() async {
  final url = Uri.parse('https://viacep.com.br/ws/01001000/json/');

  final response = await http.get(url);

  ViaCepModel? cep;
  if (response.statusCode == 200) {
    cep = ViaCepModel.fromJson(response.body);
  }

  print("CEP: $cep");
}

Bem mais simples né?
Basta chamar o 'ViaCepModel.fromJson(response.body)' que tudo funciona como antes, só que com uma 'main' bem mais limpa!

Código final

import 'package:http/http.dart' as http;
import 'dart:convert';

class ViaCepModel {
  final String cep;
  final String logradouro;
  final String bairro;
  final String localidade;
  final String uf;

  ViaCepModel({
    required this.cep,
    required this.logradouro,
    required this.bairro,
    required this.localidade,
    required this.uf,
  });

  factory ViaCepModel.fromMap(Map<String, dynamic> map) {
    return ViaCepModel(
      cep: map["cep"],
      logradouro: map['logradouro'],
      bairro: map['bairro'],
      localidade: map['localidade'],
      uf: map['uf'],
    );
  }

  factory ViaCepModel.fromJson(String data) {
    return ViaCepModel.fromMap(json.decode(data));
  }

  @override
  String toString() {
    return "ViaCepModel(cep: $cep, logradouro: $logradouro, bairro: $bairro, localidade: $localidade, uf: $uf)";
  }
}

Future<void> main() async {
  final url = Uri.parse('https://viacep.com.br/ws/01001000/json/');

  final response = await http.get(url);

  ViaCepModel? cep;
  if (response.statusCode == 200) {
    cep = ViaCepModel.fromJson(response.body);
  }

  print("CEP: $cep");
}

Considerações finais

Eu acho que é isso né ?
Não!

Temos mais coisas para fazer, aguarde os próximos posts...

Carregando publicação patrocinada...