DEV Community

CPFHub
CPFHub

Posted on

Campo de CPF em Flutter: TextInputFormatter, validação e chamada segura ao backend

Transparência: sou fundador da CPFHub, citada no final. O formatter e o validador funcionam sem nenhuma API.

Em apps Flutter de fintech, delivery ou marketplace, o CPF aparece logo no onboarding. Este guia mostra como fazer uma máscara que não briga com o cursor, validar dígitos verificadores e consultar dados sem colocar chave de API dentro do app.

1. Validador em Dart

String soDigitos(String s) => s.replaceAll(RegExp(r'\D'), '');

bool cpfValido(String entrada) {
  final d = soDigitos(entrada);
  if (d.length != 11 || RegExp(r'^(\d)\1{10}$').hasMatch(d)) return false;
  for (var t = 9; t < 11; t++) {
    var soma = 0;
    for (var i = 0; i < t; i++) {
      soma += int.parse(d[i]) * (t + 1 - i);
    }
    if (int.parse(d[t]) != (soma * 10) % 11 % 10) return false;
  }
  return true;
}
Enter fullscreen mode Exit fullscreen mode

2. Um TextInputFormatter de CPF

import 'package:flutter/services.dart';

class CpfInputFormatter extends TextInputFormatter {
  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldV, TextEditingValue newV) {
    final d = soDigitos(newV.text);
    final limitado = d.length > 11 ? d.substring(0, 11) : d;
    final buf = StringBuffer();
    for (var i = 0; i < limitado.length; i++) {
      if (i == 3 || i == 6) buf.write('.');
      if (i == 9) buf.write('-');
      buf.write(limitado[i]);
    }
    final texto = buf.toString();
    return TextEditingValue(
      text: texto,
      selection: TextSelection.collapsed(offset: texto.length),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Colocar o cursor no final é a escolha mais simples e funciona para quem digita do início ao fim, que é o caso típico.

3. O campo

TextFormField(
  keyboardType: TextInputType.number,
  inputFormatters: [CpfInputFormatter()],
  autofillHints: const [], // não deixe o teclado sugerir dados aleatórios
  decoration: const InputDecoration(labelText: 'CPF', hintText: '000.000.000-00'),
  autovalidateMode: AutovalidateMode.onUserInteraction,
  validator: (v) {
    final d = soDigitos(v ?? '');
    if (d.isEmpty) return 'Informe o CPF';
    if (d.length < 11) return 'O CPF tem 11 dígitos';
    if (!cpfValido(d)) return 'CPF inválido';
    return null;
  },
)
Enter fullscreen mode Exit fullscreen mode

TextInputType.number abre o teclado numérico no Android e no iOS. Envie para o backend só os dígitos (soDigitos).

4. Nunca coloque a chave da API no app

Tudo que vai no APK ou IPA pode ser extraído. Ofuscação, --dart-define ou arquivo .env empacotado não protegem segredo. Quem descompila o app obtém sua chave e passa a consultar CPFs na sua conta.

O desenho correto:

App Flutter  --(token do usuário)-->  Seu backend  --(x-api-key)-->  API de CPF
Enter fullscreen mode Exit fullscreen mode

O backend autentica o usuário, aplica limite por conta e só então consulta o provedor.

5. Chamando o seu backend

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

Future<String?> confirmarNome(String cpf, String tokenUsuario) async {
  final res = await http
      .post(
        Uri.parse('https://seu-backend.com/v1/verificar-cpf'),
        headers: {
          'Authorization': 'Bearer $tokenUsuario',
          'Content-Type': 'application/json',
        },
        body: jsonEncode({'cpf': soDigitos(cpf)}),
      )
      .timeout(const Duration(seconds: 12));

  if (res.statusCode == 200) return jsonDecode(res.body)['primeiroNome'] as String?;
  if (res.statusCode == 404) return null;
  throw Exception('Falha ao verificar (${res.statusCode})');
}
Enter fullscreen mode Exit fullscreen mode

No backend, a chamada ao provedor é algo como GET https://api.cpfhub.io/cpf/{cpf} com o header x-api-key.

6. UX do resultado

  • Mostre "Você é Maria?" com botões Sim/Não, em vez de exibir nome completo e data de nascimento.
  • Se o CPF não for encontrado, não acuse o usuário: peça para conferir os números e ofereça seguir com revisão manual.
  • Em caso de erro de rede, deixe continuar e marque o cadastro como pendente de verificação.

7. Testes

Teste o formatter com formatEditUpdate direto e o validador com uma tabela de casos (529.982.247-25 válido, 111.111.111-11 inválido, entrada vazia, entrada com letras).

Sobre a CPFHub

No backend, a CPFHub.io retorna nome, gênero e data de nascimento a partir do CPF em ~150 ms típicos (não SLA), com 50 créditos grátis por mês.

Top comments (0)