indo_form_validator
A lightweight form validation library for Indonesian applications. Provides validators for phone numbers, NIK, NPWP, and email with bilingual error messages.
Features
- Phone number validation (Indonesian formats: 08xx, +62, 62)
- NIK validation (16 digits with optional region code checking)
- NPWP validation (15 digits with formatting support)
- Email validation (RFC 5322 compliant)
- Bilingual error messages (Indonesian and English)
- Validator chaining and custom validators
- Strict validation mode
- Null-safe and zero dependencies
- Direct Flutter Form integration
Installation
Add to your pubspec.yaml:
dependencies:
indo_form_validator: ^1.0.0
Then run:
flutter pub get
Usage
Basic Form Validation
import 'package:indo_form_validator/indo_form_validator.dart';
TextFormField(
decoration: const InputDecoration(labelText: 'Phone Number'),
validator: IndoValidator.phone(),
)
TextFormField(
decoration: const InputDecoration(labelText: 'NIK'),
validator: IndoValidator.nik(),
)
TextFormField(
decoration: const InputDecoration(labelText: 'NPWP'),
validator: IndoValidator.npwp(),
)
TextFormField(
decoration: const InputDecoration(labelText: 'Email'),
validator: IndoValidator.email(),
)
English Error Messages
TextFormField(
validator: IndoValidator.phone(
config: ValidationConfig.english,
),
)
Strict Validation Mode
// NIK with region code validation
TextFormField(
validator: IndoValidator.nik(
config: ValidationConfig.strictMode,
validateRegion: true,
),
)
// NPWP with format validation
TextFormField(
validator: IndoValidator.npwp(
config: ValidationConfig.strictMode,
),
)
Validator Chaining
TextFormField(
validator: IndoValidator.chain([
IndoValidator.phone(),
(value) {
final cleaned = value?.replaceAll(RegExp(r'[\s\-]'), '');
return cleaned?.length == 12 ? null : 'Must be 12 digits';
},
]),
)
Custom Validators
TextFormField(
validator: IndoValidator.custom(
(value) => value?.startsWith('08') ?? false,
'Phone must start with 08',
),
)
Standalone Validation
// Using ValidationResult
final result = IndoValidator.validatePhone('081234567890');
if (result.isValid) {
print('Phone is valid');
} else {
print('Error: ${result.message}');
}
// Using boolean check
if (PhoneValidator.isValid('081234567890')) {
print('Valid phone number');
}
// Direct validation
final nikResult = NikValidator.validate('3201234567890123');
print(nikResult.isValid); // true or false
print(nikResult.message); // error message or null
NPWP Formatting
final formatted = NpwpValidator.format('123456789012345');
print(formatted); // 12.345.678.9-012.345
Validation Rules
Phone Number
- Supports formats:
08xxxxxxxxxx,+628xxxxxxxxxx,628xxxxxxxxxx - Length: 10-13 digits after prefix
- Automatically removes spaces, dashes, and parentheses
NIK (Nomor Induk Kependudukan)
- Exactly 16 digits
- Numeric only
- Optional region code validation (province: 11-94)
- Supports formatted input with spaces/dashes
NPWP (Nomor Pokok Wajib Pajak)
- Exactly 15 digits
- Numeric only
- Supports formatted (
XX.XXX.XXX.X-XXX.XXX) and non-formatted input - Strict mode validates format structure
- RFC 5322 compliant regex
- Strict mode checks for consecutive dots and valid TLD length
API Reference
IndoValidator
Main validator class providing convenient access to all validators.
// Form validators (returns String? for FormField)
IndoValidator.phone({ValidationConfig config})
IndoValidator.nik({ValidationConfig config, bool validateRegion})
IndoValidator.npwp({ValidationConfig config})
IndoValidator.email({ValidationConfig config})
// Validation methods (returns ValidationResult)
IndoValidator.validatePhone(String? value, {ValidationConfig config})
IndoValidator.validateNik(String? value, {ValidationConfig config, bool validateRegion})
IndoValidator.validateNpwp(String? value, {ValidationConfig config})
IndoValidator.validateEmail(String? value, {ValidationConfig config})
// Utility methods
IndoValidator.chain(List<String? Function(String?)> validators)
IndoValidator.custom(bool Function(String?) validationFunction, String errorMessage)
Individual Validators
Each validator has three methods:
// Returns ValidationResult with isValid and message
PhoneValidator.validate(String? value, {ValidationConfig config})
// Returns String? for FormField.validator
PhoneValidator.validator({ValidationConfig config})
// Returns bool for quick checks
PhoneValidator.isValid(String? value)
ValidationConfig
const ValidationConfig({
String language = 'id', // 'id' or 'en'
bool strict = false,
})
// Predefined configs
ValidationConfig.defaultConfig // Indonesian, non-strict
ValidationConfig.english // English, non-strict
ValidationConfig.strictMode // Indonesian, strict
ValidationResult
class ValidationResult {
final bool isValid;
final String? message;
const ValidationResult.valid();
const ValidationResult.invalid(String message);
}
Complete Example
class MyForm extends StatefulWidget {
@override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
validator: IndoValidator.phone(),
),
TextFormField(
decoration: const InputDecoration(labelText: 'NIK'),
validator: IndoValidator.nik(),
),
TextFormField(
decoration: const InputDecoration(labelText: 'NPWP'),
validator: IndoValidator.npwp(),
),
TextFormField(
decoration: const InputDecoration(labelText: 'Email'),
validator: IndoValidator.email(),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Form is valid
}
},
child: const Text('Submit'),
),
],
),
);
}
}
Testing
Run tests:
flutter test
The package includes comprehensive unit tests covering valid and invalid inputs, edge cases, error messages in both languages, and strict mode validation.
Contributing
Contributions are welcome. Please read CONTRIBUTING.md for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
See CHANGELOG.md for a list of changes.