Skip to content

Commit 684d899

Browse files
add some migration items and the validator bic
1 parent e12df52 commit 684d899

File tree

5 files changed

+196
-21
lines changed

5 files changed

+196
-21
lines changed

README-updated.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ TextFormField(
149149

150150
### Finance validators
151151

152-
- TODO [ ] `FormBuilderValidators.bic()` - requires the field's to be a valid BIC.
152+
- `FormBuilderValidators.bic()`: Checks if the field contains a valid BIC (Bank Identifier Code).
153153
- TODO [ ] `FormBuilderValidators.iban()` - requires the field's to be a valid IBAN.
154154

155155
### Generic Type Validators

doc/migration.md

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,13 +583,57 @@ Validators.maxFileSize(
583583
maxFileSizeMsg: (_, __, ___)=>'error text',
584584
);
585585
```
586-
TODO continue from here!!!
587-
- `FormBuilderValidators.mimeType()` - requires the field's value to a valid MIME type.
588-
- `FormBuilderValidators.path()` - requires the field's to be a valid file or folder path.
586+
- `FormBuilderValidators.mimeType()`: there is no equivalent to [this validator](https://github.com/flutter-form-builder-ecosystem/form_builder_validators/blob/17e982bb849dc68365f8fbc93d5a2323ee891c89/lib/src/file/mime_type_validator.dart#L47). Something close would be:
587+
```dart
588+
// Old API:
589+
FormBuilderValidators.mimeType(
590+
// This regex is the same as the default
591+
regex: RegExp(r'^[a-zA-Z0-9!#$&^_-]+\/[a-zA-Z0-9!#$&^_-]+$'),
592+
errorText: 'error text',
593+
);
594+
595+
// New API:
596+
Validators.match(
597+
RegExp(r'^[a-zA-Z0-9!#$&^_-]+\/[a-zA-Z0-9!#$&^_-]+$'),
598+
matchMsg: (_)=>'error text'
599+
);
600+
```
601+
- `FormBuilderValidators.path()`: there is no equivalent to [this validator](https://github.com/flutter-form-builder-ecosystem/form_builder_validators/blob/17e982bb849dc68365f8fbc93d5a2323ee891c89/lib/src/file/path_validator.dart#L46). Something close would be:
602+
```dart
603+
// Old API:
604+
FormBuilderValidators.path(
605+
errorText: 'error text',
606+
);
607+
608+
// New API:
609+
Validators.match(
610+
RegExp(r'^((\/|\\|[a-zA-Z]:\/)?([^<>:"|?*]+(\/|\\)?)+)$'),
611+
matchMsg: (_)=>'error text'
612+
);
613+
```
589614

590615
### Finance validators
591616

592-
- `FormBuilderValidators.bic()` - requires the field's to be a valid BIC.
617+
- `FormBuilderValidators.bic()`
618+
```dart
619+
// Old API (no regex):
620+
FormBuilderValidators.bic(
621+
errorText: 'error text',
622+
);
623+
624+
// New API:
625+
Validators.bic(
626+
bicMsg: (_)=>'error text'
627+
);
628+
629+
//------------------------------------------------------------------------------
630+
// Old API (with regex):
631+
FormBuilderValidators.bic(regex: someRegex);
632+
633+
// New API:
634+
Validators.bic(isBic: (input)=>someRegex.hasMatch(input));
635+
```
636+
TODO continue from here!!!
593637
- `FormBuilderValidators.creditCardCVC()` - requires the field's value to be a valid credit card CVC number.
594638
- `FormBuilderValidators.creditCardExpirationDate()` - requires the field's value to be a valid credit card expiration date and can check if not expired yet.
595639
- `FormBuilderValidators.creditCard()` - requires the field's value to be a valid credit card number.

lib/src/form_builder_validators.dart

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,9 +2059,9 @@ final class Validators {
20592059
/// ```dart
20602060
/// // Basic required field validation
20612061
/// final validator = Validators.required<String>();
2062-
/// print(validator(null)); // Returns localized error message
2063-
/// print(validator('')); // Returns localized error message
2064-
/// print(validator('value')); // Returns null (validation passed)
2062+
/// assert(validator(null) != null); // Returns localized error message
2063+
/// assert(validator('') != null); // Returns localized error message
2064+
/// assert(validator('value') == null); // Returns null (validation passed)
20652065
///
20662066
/// // Chaining with another validator
20672067
/// final complexValidator = Validators.required<String>(
@@ -2122,9 +2122,9 @@ final class Validators {
21222122
/// );
21232123
///
21242124
/// // Usage with different inputs
2125-
/// print(validator(null)); // Returns: null (valid)
2126-
/// print(validator('')); // Returns: null (valid)
2127-
/// print(emailValidator('invalid@email')); // Returns: error message
2125+
/// assert(validator(null) == null); // Returns: null (valid)
2126+
/// assert(validator('') == null); // Returns: null (valid)
2127+
/// assert(emailValidator('invalid@email') != null); // Returns: error message
21282128
/// ```
21292129
///
21302130
/// ## Caveats
@@ -2171,13 +2171,13 @@ final class Validators {
21712171
/// final defaultValue = 'default value';
21722172
/// final validator = Validators.validateWithDefault('N/A', minLength);
21732173
///
2174-
/// print(validator(null)); // Returns null (valid)
2175-
/// print(validator('ab')); // Returns 'Must be at least 3 characters'
2176-
/// print(validator('abc')); // Returns null (valid)
2174+
/// assert(validator(null) == null); // Returns null (valid)
2175+
/// assert(validator('ab') != null); // Returns 'Must be at least 3 characters'
2176+
/// assert(validator('abc') == null); // Returns null (valid)
21772177
/// // Equivalent to:
2178-
/// print(minLength(null ?? defaultValue)); // Returns null (valid)
2179-
/// print(minLength('ab' ?? defaultValue)); // Returns 'Must be at least 3 characters'
2180-
/// print(minLength('abc' ?? defaultValue)); // Returns null (valid)
2178+
/// assert(minLength(null ?? defaultValue) == null); // Returns null (valid)
2179+
/// assert(minLength('ab' ?? defaultValue) != null); // Returns 'Must be at least 3 characters'
2180+
/// assert(minLength('abc' ?? defaultValue) == null); // Returns null (valid)
21812181
/// ```
21822182
/// {@endtemplate}
21832183
static Validator<T?> validateWithDefault<T extends Object>(
@@ -4100,11 +4100,61 @@ final class Validators {
41004100
/// ```
41014101
/// {@endtemplate}
41024102
static Validator<String> creditCard({
4103+
// TODO(ArturAssisComp): turn this into a function, becoming more generic.
41034104
RegExp? regex,
41044105
String Function(String input)? creditCardMsg,
41054106
}) =>
41064107
val.creditCard(regex: regex, creditCardMsg: creditCardMsg);
41074108

4109+
///{@template validator_bic}
4110+
/// Creates a validator that checks if a string is a valid BIC (Bank Identifier Code).
4111+
///
4112+
/// A BIC validator checks string inputs against standard BIC format regulations. The
4113+
/// validator returns `null` for valid BICs and an error message for invalid inputs.
4114+
///
4115+
/// BIC codes must consist of 8 or 11 characters: 4 bank code letters, 2 country code
4116+
/// letters, 2 location code alphanumeric characters, and optionally 3 branch code
4117+
/// alphanumeric characters.
4118+
///
4119+
/// ## Parameters
4120+
/// - `isBic` (`bool Function(String input)?`): Optional custom function to determine
4121+
/// if a string is a valid BIC. If provided, this function overrides the default
4122+
/// BIC validation logic.
4123+
/// - `bicMsg` (`String Function(String input)?`): Optional custom function to generate
4124+
/// error messages for invalid BICs. If provided, this function overrides the default
4125+
/// error message.
4126+
///
4127+
/// ## Returns
4128+
/// A `Validator<String>` function that returns:
4129+
/// - `null` if the input is a valid BIC
4130+
/// - An error message string if the input is not a valid BIC
4131+
///
4132+
/// ## Examples
4133+
/// ```dart
4134+
/// // Using default validation
4135+
/// final validator = bic();
4136+
/// assert(validator('DEUTDEFF') == null); // Valid: 8-character BIC
4137+
/// assert(validator('DEUTDEFFXXX') == null); // Valid: 11-character BIC
4138+
/// assert(validator('deut deff xxx') == null); // Valid: spaces are removed and case is normalized
4139+
/// assert(validator('DEUT123') != null); // Invalid: too short
4140+
/// assert(validator('DEUTDEFFXXXX') != null); // Invalid: too long
4141+
/// assert(validator('123TDEFF') != null); // Invalid: first 4 chars must be letters
4142+
///
4143+
/// // Using custom validation and error message
4144+
/// final validator = bic(
4145+
/// isBic: (value) => value.startsWith('DEUT'),
4146+
/// bicMsg: (value) => 'BIC must start with DEUT, got: $value',
4147+
/// );
4148+
/// assert(validator('DEUTDEFF') == null); // Valid: starts with DEUT
4149+
/// assert(validator('ABCDDEFF') != null); // Invalid: custom error message
4150+
/// ```
4151+
///{@endtemplate}
4152+
static Validator<String> bic({
4153+
c.bool Function(String input)? isBic,
4154+
String Function(String input)? bicMsg,
4155+
}) =>
4156+
val.bic(isBic: isBic, bicMsg: bicMsg);
4157+
41084158
// Network validators
41094159

41104160
/// {@template validator_ip}
@@ -4131,8 +4181,8 @@ final class Validators {
41314181
/// ```dart
41324182
/// // Basic IPv4 validation
41334183
/// final ipv4Validator = ip();
4134-
/// print(ipv4Validator('192.168.1.1')); // null (valid)
4135-
/// print(ipv4Validator('256.1.2.3')); // Returns error message (invalid)
4184+
/// assert(ipv4Validator('192.168.1.1') == null); // null (valid)
4185+
/// assert(ipv4Validator('256.1.2.3') != null); // Returns error message (invalid)
41364186
///
41374187
/// // Custom error message for IPv6
41384188
/// final ipv6Validator = ip(
@@ -4188,14 +4238,14 @@ final class Validators {
41884238
/// ```dart
41894239
/// // Basic URL validation
41904240
/// final validator = url();
4191-
/// print(validator('https://example.com')); // Returns: null
4241+
/// assert(validator('https://example.com') == null); // Returns: null
41924242
///
41934243
/// // Custom protocol validation
41944244
/// final ftpValidator = url(
41954245
/// protocols: ['ftp'],
41964246
/// requireProtocol: true
41974247
/// );
4198-
/// print(ftpValidator('ftp://server.com')); // Returns: null
4248+
/// assert(ftpValidator('ftp://server.com') == null); // Returns: null
41994249
///
42004250
/// // With host filtering
42014251
/// final restrictedValidator = url(

lib/src/validators/finance_validators.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ Validator<String> creditCard({
1818
};
1919
}
2020

21+
/// {@macro validator_bic}
22+
Validator<String> bic({
23+
bool Function(String input)? isBic,
24+
String Function(String input)? bicMsg,
25+
}) {
26+
return (String input) {
27+
return (isBic?.call(input) ?? _isBIC(input))
28+
? null
29+
: (bicMsg?.call(input) ??
30+
FormBuilderLocalizations.current.bicErrorText);
31+
};
32+
}
33+
2134
//******************************************************************************
2235
//* Aux functions *
2336
//******************************************************************************
@@ -51,3 +64,21 @@ bool _isCreditCard(String value, RegExp regex) {
5164

5265
return (sum % 10 == 0);
5366
}
67+
68+
/// Check if the string is a valid BIC string.
69+
///
70+
/// ## Parameters:
71+
/// - [value] The string to be evaluated.
72+
///
73+
/// ## Returns:
74+
/// A boolean indicating whether the value is a valid BIC.
75+
bool _isBIC(String value) {
76+
final String bic = value.replaceAll(' ', '').toUpperCase();
77+
final RegExp regex = RegExp(r'^[A-Z]{4}[A-Z]{2}\w{2}(\w{3})?$');
78+
79+
if (bic.length != 8 && bic.length != 11) {
80+
return false;
81+
}
82+
83+
return regex.hasMatch(bic);
84+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import 'package:flutter_test/flutter_test.dart';
2+
import 'package:form_builder_validators/form_builder_validators.dart';
3+
import 'package:form_builder_validators/src/validators/finance_validators.dart';
4+
5+
void main() {
6+
group('Validator: bic', () {
7+
final List<({bool fails, String input})> testCases =
8+
<({bool fails, String input})>[
9+
(
10+
input: '',
11+
fails: true,
12+
),
13+
(
14+
input: 'DEUTDEFF',
15+
fails: false,
16+
),
17+
(
18+
input: 'DEUTDEFF500',
19+
fails: false,
20+
),
21+
(
22+
input: 'INVALIDBIC',
23+
fails: true,
24+
),
25+
];
26+
27+
final Validator<String> v = bic();
28+
for (final (input: String input, fails: bool fails) in testCases) {
29+
test('should ${fails ? 'fail' : 'pass'} for input "$input"', () {
30+
expect(v(input),
31+
fails ? FormBuilderLocalizations.current.bicErrorText : isNull);
32+
});
33+
}
34+
35+
test('should return custom error msg', () {
36+
final Validator<String> v = bic(bicMsg: (_) => 'error text');
37+
38+
expect(v('BOTKJPJTXXX'), isNull);
39+
expect(v('BOTKJPJTXX'), equals('error text'));
40+
});
41+
42+
test('should use the custom logic to check if the input is a valid bic',
43+
() {
44+
final Validator<String> v =
45+
bic(isBic: (String input) => input.length == 3);
46+
expect(v('abc'), isNull);
47+
expect(v('abc '), equals(FormBuilderLocalizations.current.bicErrorText));
48+
});
49+
});
50+
}

0 commit comments

Comments
 (0)