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...