Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,30 @@ TextFormField(

### Formatters

| Padrão | Formatter | Formato
|:------------------|:-------------------------------|:----------------
| Altura | AlturaInputFormatter() | 2,22
| Cartão | CartaoBancarioInputFormatter() | 0000 1111 2222 3333 4444
| Centavos | CentavosInputFormatter() | 7,194
| CEP | CepInputFormatter() | 99.999-999
| CPF | CpfInputFormatter() | 999.999.99-99
| CNPJ | CnpjInputFormatter() | 99.999.999/9999-99
| CPF / CNPJ | CpfOuCnpjFormatter() | se adapta conforme os números são inseridos
| CEST | CESTInputFormatter() | 12.345.67
| CNS | CNSInputFormatter() | 111 2222 3333 4444
| Data | DataInputFormatter() | 01/01/1900
| Hora | HoraInputFormatter() | 23:59
| IOF | HoraInputFormatter() | 1,234567
| KM | KmInputFormatter() | 999.999
| Cert. nascimento | CertNascimentoInputFormatter() | 000000 11 22 3333 4 55555 666 7777777 88
| NCM | NCMInputFormatter() | 1234.56.78
| Peso | PesoInputFormatter() | 111,1
| Placa | PlacaVeiculoInputFormatter() | AAA-1234 (**não** utilizar `FilteringTextInputFormatter.digitsOnly`)
| Real | RealInputFormatter() | 20.550
| Telefone | TelefoneInputFormatter() | (99) 9999-9999
| Validade cartão | ValidadeCartaoInputFormatter() | 12/24 ou 12/2024
| Temperatura | TemperaturaInputFormatter() |27,1
| Padrão | Formatter | Formato |
|:-----------------|:-------------------------------|:---------------------------------------------------------------------|
| Altura | AlturaInputFormatter() | 2,22 |
| Cartão | CartaoBancarioInputFormatter() | 0000 1111 2222 3333 4444 |
| Centavos | CentavosInputFormatter() | 7,194 |
| CEP | CepInputFormatter() | 99.999-999 |
| CPF | CpfInputFormatter() | 999.999.99-99 |
| CNPJ | CnpjInputFormatter() | 99.999.999/9999-99 |
| CPF / CNPJ | CpfOuCnpjFormatter() | se adapta conforme os números são inseridos |
| CEST | CESTInputFormatter() | 12.345.67 |
| CNS | CNSInputFormatter() | 111 2222 3333 4444 |
| Data | DataInputFormatter() | 01/01/1900 |
| Hora | HoraInputFormatter() | 23:59 |
| IOF | HoraInputFormatter() | 1,234567 |
| KM | KmInputFormatter() | 999.999 |
| Cert. nascimento | CertNascimentoInputFormatter() | 000000 11 22 3333 4 55555 666 7777777 88 |
| NCM | NCMInputFormatter() | 1234.56.78 |
| NUP | NUPInputFormatter() | 1234567-89.0123.4.56.7890 |
| Peso | PesoInputFormatter() | 111,1 |
| Placa | PlacaVeiculoInputFormatter() | AAA-1234 (**não** utilizar `FilteringTextInputFormatter.digitsOnly`) |
| Real | RealInputFormatter() | 20.550 |
| Telefone | TelefoneInputFormatter() | (99) 9999-9999 |
| Validade cartão | ValidadeCartaoInputFormatter() | 12/24 ou 12/2024 |
| Temperatura | TemperaturaInputFormatter() | 27,1 |

### Modelos

Expand Down Expand Up @@ -97,6 +98,7 @@ Métodos que facilitam manipular valores:
- `UtilBrasilFields.obterCnpj('11222333444455')` (11.222.333/4444-55)
- `UtilBrasilFields.obterCep('11222333')` (11.222-333)
- `UtilBrasilFields.obterCep('11222333', ponto: false)` (11222-333)
- `UtilBrasilFields.obterNUP('06010642120226000000')` (0601064-21.2022.6.00.0000)
- `UtilBrasilFields.obterTelefone('00999998877')` ((00) 99999-8877)
- `UtilBrasilFields.obterTelefone('(00) 99999-8877', mascara: false)` (00999998877)
- `UtilBrasilFields.obterTelefone('999998877', ddd: false)` (99999-8877)
Expand All @@ -112,6 +114,7 @@ Métodos que facilitam manipular valores:
- `UtilBrasilFields.converterMoedaParaDouble` (remove o R$ e retorna um double)
- `UtilBrasilFields.isCPFValido` (retorna `true` se o CPF for válido, caso contrário, retorna `false`)
- `UtilBrasilFields.isCNPJValido` (retorna `true` se o CNPJ for válido, caso contrário, retorna `false`)
- `UtilBrasilFields.isNUPValido` (retorna `true` se o NUP for válido, caso contrário, retorna `false`)

Para inicializar um `TextEditingController` com o texto já formatado, basta escolher o método com o formato desejado e setar no atributo `text`:

Expand Down
4 changes: 4 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ class MyApp extends StatelessWidget {
label: 'NCM',
formatter: NCMInputFormatter(),
),
RowFormatters(
label: 'NUP',
formatter: NUPInputFormatter(),
),
RowFormatters(
label: 'CEST',
formatter: CESTInputFormatter(),
Expand Down
1 change: 1 addition & 0 deletions lib/brasil_fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export 'src/formatters/hora_input_formatter.dart';
export 'src/formatters/iof_input_formatter.dart';
export 'src/formatters/km_input_formatter.dart';
export 'src/formatters/ncm_input_formatter.dart';
export 'src/formatters/nup_input_formatter.dart';
export 'src/formatters/peso_input_formatter.dart';
export 'src/formatters/placa_veiculo_formatter.dart';
export 'src/formatters/real_input_formatter.dart';
Expand Down
46 changes: 46 additions & 0 deletions lib/src/formatters/nup_input_formatter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'package:flutter/services.dart';

/// Formata o valor do campo com a máscara de NUP (Numeração Única de Processos): `XXXXXXX-XX.XXXX.X.XX.XXXX`
/// Referência: [Documentação CNJ](https://www.cnj.jus.br/programas-e-acoes/numeracao-unica/)
class NUPInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
// verifica o tamanho máximo do campo
if (newValue.text.length > 20) return oldValue;

var posicaoCursor = newValue.selection.end;
var substrIndex = 0;
final valorFinal = StringBuffer();

if (newValue.text.length >= 8) {
valorFinal.write('${newValue.text.substring(0, substrIndex = 7)}-');
if (newValue.selection.end >= 7) posicaoCursor++;
}
if (newValue.text.length >= 10) {
valorFinal.write('${newValue.text.substring(7, substrIndex = 9)}.');
if (newValue.selection.end >= 9) posicaoCursor++;
}
if (newValue.text.length >= 14) {
valorFinal.write('${newValue.text.substring(9, substrIndex = 13)}.');
if (newValue.selection.end >= 13) posicaoCursor++;
}
if (newValue.text.length >= 15) {
valorFinal.write('${newValue.text.substring(13, substrIndex = 14)}.');
if (newValue.selection.end >= 14) posicaoCursor++;
}
if (newValue.text.length >= 17) {
valorFinal.write('${newValue.text.substring(14, substrIndex = 16)}.');
if (newValue.selection.end >= 16) posicaoCursor++;
}

if (newValue.text.length >= substrIndex) {
valorFinal.write(newValue.text.substring(substrIndex));
}

return TextEditingValue(
text: valorFinal.toString(),
selection: TextSelection.collapsed(offset: posicaoCursor),
);
}
}
10 changes: 10 additions & 0 deletions lib/src/util/util_brasil_fields.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import '../formatters/adiciona_separador.dart';
import '../validators/nup_validator.dart';
import '../validators/validators.dart';

class UtilBrasilFields {
Expand Down Expand Up @@ -76,6 +77,9 @@ class UtilBrasilFields {
///Faz a validação do CNPJ retornando `[true]` ou `[false]`
static bool isCNPJValido(String? cnpj) => CNPJValidator.isValid(cnpj);

///Faz a validação do NUP retornando `[true]` ou `[false]`
static bool isNUPValido(String? nup) => NUPValidator.isValid(nup);

/// Gera um CPF aleatório
///
/// Formatado ou não formatado, baseado no parâmetro `useFormat`:
Expand Down Expand Up @@ -139,6 +143,12 @@ class UtilBrasilFields {
return CNPJValidator.strip(cnpj).substring(12);
}

/// Retorna o NUP informado, utilizando a máscara: `NNNNNNN-DD.AAAA.J.TR.OOOO`
static String obterNUP(String nup) {
assert(isNUPValido(nup), 'Número de Processo inválido!');
return NUPValidator.format(nup);
}

/// Retorna o número real informado, utilizando a máscara: `R$ 50.000,00` ou `50.000,00`
static String obterReal(double value, {bool moeda = true, int decimal = 2}) {
bool isNegative = false;
Expand Down
52 changes: 52 additions & 0 deletions lib/src/validators/nup_validator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class NUPValidator {
static const stripRegex = r'[^\d]';

static bool isValid(String? nup, {stripBeforeValidation = true}) {
if (stripBeforeValidation) {
nup = strip(nup);
}

if (nup == null || nup.isEmpty) {
return false;
}

if (nup.length != 20) {
return false;
}

final checkDigit = _checkDigit(nup);
return nup.substring(7, 9) == checkDigit.toString();
}

static String strip(String? nup) {
var regex = RegExp(stripRegex);
nup = nup ?? '';

return nup.replaceAll(regex, '');
}

// Compute the Check Digit (or 'Dígito Verificador (DV)' in PT-BR).
// You can learn more about the algorithm on [CNJ (pt-br)](https://atos.cnj.jus.br/files/compilado23285720221017634de539229ab.pdf)
static String _checkDigit(String nup) {
final sequential = nup.substring(0, 7);
final year = nup.substring(9, 13);
final segment = nup[13];
final court = nup.substring(14, 16);
final origin = nup.substring(16);

final r1 = int.parse(sequential) % 97;
final r2 = int.parse('$r1$year$segment$court') % 97;
final r3 = int.parse('$r2${origin}00') % 97;

final checkDigit = 98 - r3;
return checkDigit.toString().padLeft(2, '0');
}

static String format(String nup) {
var regExp = RegExp(r'^(\d{7})(\d{2})(\d{4})(\d{1})(\d{2})(\d{4})$');

return strip(nup).replaceAllMapped(
regExp, (Match m) => '${m[1]}-${m[2]}.${m[3]}.${m[4]}.${m[5]}.${m[6]}');
}

}
9 changes: 9 additions & 0 deletions test/brasil_fields_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:brasil_fields/brasil_fields.dart';
import 'package:brasil_fields/src/formatters/compound_formatters/compound_formatter.dart';
import 'package:brasil_fields/src/formatters/nup_input_formatter.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -333,6 +334,14 @@ void main() {
expect(textController.text, '0309.90.00');
});

testWidgets('NUPInputFormatter', (WidgetTester tester) async {
final textController = TextEditingController();

await tester.pumpWidget(boilerplate(NUPInputFormatter(), textController));
await tester.enterText(find.byType(TextField), '12345678901234567890');
expect(textController.text, '1234567-89.0123.4.56.7890');
});

testWidgets('CESTInputFormatter', (WidgetTester tester) async {
final textController = TextEditingController();

Expand Down
28 changes: 28 additions & 0 deletions test/nup_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:brasil_fields/src/validators/nup_validator.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('Test NUP validator', () {
expect(NUPValidator.isValid('0601064-21.2022.6.00.0000'), true);
expect(NUPValidator.isValid('0601064-22.2022.6.00.0000'), false);
expect(NUPValidator.isValid('00601064-22.2022.6.00.0000'), false);
expect(NUPValidator.isValid('06010642120226000000'), true);
expect(NUPValidator.isValid('06010642220226000000'), false);
expect(NUPValidator.isValid('006010642120226000000'), false);
expect(
NUPValidator.isValid('03346teste1671002@mail',
stripBeforeValidation: false),
false);
expect(
NUPValidator.isValid('57abc803.6586-52', stripBeforeValidation: false),
false);
});

test('Test NUP formatter', () {
expect(NUPValidator.format('06010642120226000000'), '0601064-21.2022.6.00.0000');
});

test('Test NUP strip', () {
expect(NUPValidator.strip('0601064-21.2022.6.00.0000'), '06010642120226000000');
});
}
63 changes: 63 additions & 0 deletions test/src/formatters/nup_input_formatter_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'package:brasil_fields/src/formatters/nup_input_formatter.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
evaluate(String oldValue, String newValue) {
return NUPInputFormatter()
.formatEditUpdate(
TextEditingValue(text: oldValue),
TextEditingValue(text: newValue),
)
.text;
}

group('NUPInputFormatter', () {
test('padrao', () => expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890'));
test('limite 20 digitos', () => expect(evaluate('', '123456789012345678901'), ''));
test('backspace', () {
expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890');
expect(evaluate('', '1234567890123456789'), '1234567-89.0123.4.56.789');
expect(evaluate('', '123456789012345678'), '1234567-89.0123.4.56.78');
expect(evaluate('', '12345678901234567'), '1234567-89.0123.4.56.7');
expect(evaluate('', '1234567890123456'), '1234567-89.0123.4.56');
expect(evaluate('', '123456789012345'), '1234567-89.0123.4.5');
expect(evaluate('', '12345678901234'), '1234567-89.0123.4');
expect(evaluate('', '1234567890123'), '1234567-89.0123');
expect(evaluate('', '123456789012'), '1234567-89.012');
expect(evaluate('', '12345678901'), '1234567-89.01');
expect(evaluate('', '1234567890'), '1234567-89.0');
expect(evaluate('', '123456789'), '1234567-89');
expect(evaluate('', '12345678'), '1234567-8');
expect(evaluate('', '1234567'), '1234567');
expect(evaluate('', '123456'), '123456');
expect(evaluate('', '12345'), '12345');
expect(evaluate('', '1234'), '1234');
expect(evaluate('', '123'), '123');
expect(evaluate('', '12'), '12');
expect(evaluate('', '1'), '1');
});

test('digitacao', () {
expect(evaluate('', '1'), '1');
expect(evaluate('', '12'), '12');
expect(evaluate('', '123'), '123');
expect(evaluate('', '1234'), '1234');
expect(evaluate('', '12345'), '12345');
expect(evaluate('', '123456'), '123456');
expect(evaluate('', '1234567'), '1234567');
expect(evaluate('', '12345678'), '1234567-8');
expect(evaluate('', '123456789'), '1234567-89');
expect(evaluate('', '1234567890'), '1234567-89.0');
expect(evaluate('', '12345678901'), '1234567-89.01');
expect(evaluate('', '123456789012'), '1234567-89.012');
expect(evaluate('', '1234567890123'), '1234567-89.0123');
expect(evaluate('', '12345678901234'), '1234567-89.0123.4');
expect(evaluate('', '123456789012345'), '1234567-89.0123.4.5');
expect(evaluate('', '1234567890123456'), '1234567-89.0123.4.56');
expect(evaluate('', '12345678901234567'), '1234567-89.0123.4.56.7');
expect(evaluate('', '123456789012345678'), '1234567-89.0123.4.56.78');
expect(evaluate('', '1234567890123456789'), '1234567-89.0123.4.56.789');
expect(evaluate('', '12345678901234567890'), '1234567-89.0123.4.56.7890');
});
});
}
11 changes: 11 additions & 0 deletions test/util_brasil_fields_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ void main() {
expect(UtilBrasilFields.removeCaracteres(cep), '11222333');
});

test('NUP', () {
const nup = '0601064-21.2022.6.00.0000';
expect(UtilBrasilFields.removeCaracteres(nup), '06010642120226000000');
});

test('Real', () {
const real = '11.222';
expect(UtilBrasilFields.removeCaracteres(real), '11222');
Expand Down Expand Up @@ -263,6 +268,12 @@ void main() {
expect(UtilBrasilFields.obterCnpjDiv(cpnjComMascara), '90');
});

test('Obter NUP', () {
const nupSemMascara = '06010642120226000000';
const nupComMascara = '0601064-21.2022.6.00.0000';
expect(UtilBrasilFields.obterNUP(nupSemMascara), nupComMascara);
});

group('Obter Real', () {
test('com moeda (R\$)', () {
const real = 85437107.04;
Expand Down
Loading