dup 1.0.4
dup: ^1.0.4 copied to clipboard
A powerful, flexible schema-based validation library for Dart and Flutter, inspired by JavaScript's yup.
dup #
A powerful, flexible validation library for Dart and Flutter, inspired by JavaScript's yup[1].
Features #
- Schema-based validation for forms and data models
- Chainable, composable validation rules
- Customizable error messages and labels
- Exception-based error handling
- Easy localization (multi-language support)
- Supports Korean postpositions and number formatting
- Easy integration with Flutter
Usage #
dup enables you to define validation rules for your data using a schema-based approach, making your validation logic clean, reusable, and maintainable[2].
1. Define a validation schema #
import 'package:dup/dup.dart';
final BaseValidatorSchema schema = BaseValidatorSchema({
'email': ValidateString()
.email()
.required()
.setLabel('Email'),
'password': ValidateString()
.password()
.required()
.setLabel('Password'),
'age': ValidateNumber()
.min(18)
.max(99)
.setLabel('Age'),
'tags': ValidateList()
.minLength(1)
.maxLength(5)
.hasNoDuplicates()
.setLabel('Tags'),
'categories': ValidateList()
.isNotEmpty()
.contains('general')
.setLabel('Categories'),
});
emailmust be a valid email address and is required.passwordmust meet your password rule and is required.agemust be between 18 and 99.tagsmust have 1-5 items with no duplicates.categoriesmust not be empty and must contain 'general'.
2. Prepare request data #
final request = {
'email': 'test@example.com',
'password': '1234',
'age': 20,
'tags': ['flutter', 'dart', 'mobile'],
'categories': ['general', 'programming'],
};
3. Validate #
Call useUiForm.validate() to validate your data against the schema.
If validation fails, a FormValidationException will be thrown with a map of error messages.
try {
await useUiForm.validate(schema, request);
// Validation passed!
} catch (e) {
print(e.toString()); // Example: 'Email is not a valid email address.'
}
Validation Types #
String Validation #
ValidateString()
.min(2)
.max(50)
.email()
.password()
.matches(RegExp(r'^[a-zA-Z]+$'))
.setLabel('Username')
.required()
Number Validation #
ValidateNumber()
.min(0)
.max(100)
.isInteger()
.setLabel('Score')
.required()
List Validation #
ValidateList()
.isNotEmpty() // List must not be empty
.minLength(2) // At least 2 items
.maxLength(10) // At most 10 items
.lengthBetween(2, 10) // Between 2-10 items
.hasLength(5) // Exactly 5 items
.contains('required_item') // Must contain specific item
.doesNotContain('forbidden') // Must not contain specific item
.hasNoDuplicates() // No duplicate items allowed
.all((item) => item.isNotEmpty) // All items must satisfy condition
.any((item) => item.startsWith('prefix')) // At least one item must satisfy condition
.eachItem((item) => item.length > 2 ? null : 'Item too short') // Custom validation for each item
.setLabel('Items')
.required()
List Validation Methods:
isNotEmpty(): Validates that the list contains at least one itemisEmpty(): Validates that the list is emptyminLength(int min): Validates minimum number of itemsmaxLength(int max): Validates maximum number of itemslengthBetween(int min, int max): Validates item count within rangehasLength(int length): Validates exact number of itemscontains(T item): Validates that list contains a specific itemdoesNotContain(T item): Validates that list does not contain a specific itemhasNoDuplicates(): Validates that all items in the list are uniqueall(bool Function(T) predicate): Validates that all items satisfy a conditionany(bool Function(T) predicate): Validates that at least one item satisfies a conditioneachItem(String? Function(T) validator): Applies custom validation to each item individually
Example: Validating a request object #
You can encapsulate validation logic inside your data classes for better structure and reusability.
class RequestObject {
String? title;
String? contents;
List? tags;
RequestObject({this.title, this.contents, this.tags});
/// Validates the request fields using dup's schema-based validation.
Future validate() async {
final BaseValidatorSchema schema = BaseValidatorSchema({
'title': ValidateString()
.min(2)
.setLabel('Title')
.required(),
'contents': ValidateString()
.min(2)
.setLabel('Contents')
.required(),
'tags': ValidateList()
.minLength(1)
.maxLength(10)
.eachItem((tag) => tag.trim().isEmpty ? 'Tag cannot be empty' : null)
.setLabel('Tags')
.required(),
});
await useUiForm.validate(schema, {
'title': title,
'contents': contents,
'tags': tags,
});
}
}
Custom Validation #
dup allows you to easily add your own validation logic, either inline or as reusable extensions[2].
Inline Custom Validator #
final validator = ValidateString()
.setLabel('Nickname')
.required()
.addValidator((value) {
if (value != null && value.contains('admin')) {
return 'Nickname cannot contain the word "admin".';
}
return null;
});
Custom Extension Validator #
You can define your own extension methods for custom validation rules:
extension CustomStringValidator on ValidateString {
/// Disallow special characters in the string.
ValidateString noSpecialChars({String? message}) {
return addValidator((value) {
if (value != null && RegExp(r'[!@#\$%^&*(),.?":{}|<>]').hasMatch(value)) {
return message ?? '$label cannot contain special characters.';
}
return null;
});
}
}
// Usage
final validator = ValidateString()
.setLabel('Nickname')
.noSpecialChars(message: 'Nickname cannot contain special characters!');
Custom List Validators #
extension CustomListValidator on ValidateList {
/// Validates that list contains only unique values based on a key selector
ValidateList uniqueBy(K Function(T) keySelector, {String? message}) {
return addValidator((value) {
if (value != null) {
final keys = value.map(keySelector).toList();
final uniqueKeys = keys.toSet();
if (keys.length != uniqueKeys.length) {
return message ?? '$label must contain unique items';
}
}
return null;
});
}
}
// Usage
final validator = ValidateList()
.setLabel('Users')
.uniqueBy((user) => user.email, message: 'Users must have unique email addresses');
Customizing Error Messages #
dup lets you easily override the default error messages for any validation rule[2].
You can do this in two ways:
1. Per-Validator Custom Message #
You can provide a custom message for a specific validation rule using the messageFactory
parameter:
final validator = ValidateString()
.setLabel('Username')
.min(
4, messageFactory: (label, args) => '$label must be at least ${args['min']} characters long.');
final listValidator = ValidateList()
.setLabel('Items')
.minLength(
2, messageFactory: (label, args) => '$label must contain at least ${args['min']} items.');
- The
messageFactoryis a function that receives the field label and arguments (likemin,max, etc.). - This custom message will be used only for this validation rule.
2. Global Custom Messages #
You can globally override error messages for your entire app by registering a custom message factory:
ValidatorLocale.setLocale(
ValidatorLocale(
mixed: {
'required': (args) => '${args['name']} is required.',
// ...other global messages
},
string: {
'min': (args) => '${args['name']} must be at least ${args['min']} characters long.',
// ...other string messages
},
array: {
'notEmpty': (args) => '${args['name']} cannot be empty.',
'min': (args) => '${args['name']} must have at least ${args['min']} items.',
'max': (args) => '${args['name']} cannot have more than ${args['max']} items.',
'noDuplicates': (args) => '${args['name']} must not contain duplicate items.',
// ...other array messages
},
// ...number, etc.
),
);
- This will apply your custom messages to all validators of the corresponding type and rule.
- You can still override these global messages per-validator using the
messageFactoryparameter if needed.
3. Localization File Customization #
If you use localization (e.g., with easy_localization), you can also edit your language JSON files
to change the default messages for each key.
Summary:
- Use
messageFactoryin your validator for per-field custom messages. - Use
ValidatorLocale.setLocale()for app-wide global custom messages. - Edit your localization files for language-specific message customization.
API Overview #
BaseValidator: Abstract base class for validatorsValidateString: String validation (min, max, email, password, etc.)ValidateNumber: Number validation (min, max, isInteger, etc.)ValidateList: List/Array validation (length, contents, uniqueness, etc.)BaseValidatorSchema: Schema for form validationUiFormService: Service for validating schemas and handling errorsFormValidationException: Exception thrown on validation failure
Links #
License #
MIT
[1] https://github.com/jquense/yup [2] programming.data_validation