2

Busca de CEP no Flutter que não cai quando a ViaCEP sai do ar

Quase todo app brasileiro tem um campo de CEP. E quase todo app resolve isso do
mesmo jeito: uma chamada para viacep.com.br/ws/{cep}/json, um jsonDecode e
pronto. Funciona até o dia em que não funciona.

Os problemas de chamar a ViaCEP direto

Montando o flutter_cep2, esbarrei em detalhes que pegam muita implementação
caseira:

  • CEP inexistente não é 404. A ViaCEP responde 200 com {"erro": "true"},
    e hoje o valor vem como string. Quem testa data['erro'] == true recebe um
    endereço vazio em vez de um erro.
  • Acento quebrado. A resposta vem em UTF-8 sem charset no cabeçalho. O
    response.body do package:http cai para latin1 e "São Paulo" vira
    "São Paulo". É preciso decodificar os bytes como UTF-8.
  • Um único ponto de falha. A ViaCEP é gratuita, mantida por voluntários, e
    pede para não fazer consultas em massa: IP que exagera pode ser bloqueado.
    Quando ela fica fora do ar, o cadastro do seu app para junto.
  • As bases são diferentes. O CEP 99999-999 não existe na ViaCEP, mas existe
    no OpenCEP e na BrasilAPI (Avenida das Torres, Sarandi/PR).

A solução: fallback entre provedores e cache

import 'package:flutter_cep2/flutter_cep2.dart';

final cep = FlutterCep2(
  providers: [CepProvider.viaCep, CepProvider.brasilApi, CepProvider.openCep],
  cacheDuration: const Duration(hours: 1),
);

final endereco = await cep.search('01310-100');
print('${endereco.logradouro}, ${endereco.bairro} - ${endereco.localidade}/${endereco.uf}');
// Avenida Paulista, Bela Vista - São Paulo/SP

O search tenta os provedores na ordem. Se um cai, demora demais ou não conhece
o CEP, passa para o próximo. O resultado é sempre o mesmo Cep tipado, venha de
onde vier. Com o cache ligado, a segunda consulta do mesmo CEP sai da memória,
sem rede e sem gastar o limite da ViaCEP.

Os erros têm tipo, então a mensagem certa para o usuário vira um switch:

try {
  await cep.search(entrada);
} on CepException catch (e) {
  final mensagem = switch (e.type) {
    CepErrorType.invalidFormat => 'Digite os 8 números do CEP',
    CepErrorType.notFound => 'CEP não encontrado',
    _ => 'Não deu para consultar o CEP agora, tente de novo',
  };
}

Mais coisas que vêm junto

Busca por endereço, quando o usuário não sabe o CEP:

final ruas = await cep.searchAddress('RS', 'Porto Alegre', 'Domingos Jose');
// 91790-072 Rua Domingos José Poli, 90420-200 Rua Domingos José de Almeida...

Latitude e longitude, pela BrasilAPI:

final ponto = await FlutterCep2(providers: [CepProvider.brasilApi]).search('01310-100');
print('${ponto.latitude}, ${ponto.longitude}'); // -23.5617698, -46.6553299

Utilitários offline, sem nenhuma requisição, bons para formulário:

isValidCep('01310-100'); // true
formatCep('01310100');   // '01310-100'
cepState('01310-100');   // (uf: 'SP', estado: 'São Paulo', regiao: 'Sudeste')

O cepState usa as faixas numéricas dos Correios. Conferi a tabela contra
3.676 CEPs reais das 27 UFs, sem nenhuma divergência.

Dá para usar fora do Flutter

Apesar do nome, o pacote é Dart puro: roda em Android, iOS, web, desktop, CLI e
servidor. As três APIs liberam CORS, então funciona no Flutter web também.

Instalação

flutter pub add flutter_cep2

Se te ajudar, uma estrela no GitHub e um like no pub.dev fazem o pacote
aparecer para mais gente. Issues e PRs são bem-vindos.

Carregando publicação patrocinada...
1

Uma hora de cache no CEP que deu certo faz sentido. Falta dizer se o CEP que nenhum provedor conhece também fica guardado esse tempo: um CEP recém-criado continuaria invisível até a hora acabar.

1