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;
}
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),
);
}
}
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;
},
)
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
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})');
}
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)