input_validate 1.0.1
input_validate: ^1.0.1 copied to clipboard
A pure Dart package for validating nested map data structures with wildcard support.
Input Validate #
A powerful Dart package for validating nested map data structures with Laravel-style validation rules and wildcard path support. Perfect for validating JSON data, form inputs, API payloads, and complex nested objects.
Features #
β¨ Laravel-style validation - Familiar validation syntax inspired by Laravel's request validation
π― Wildcard path support - Validate arrays and nested structures with users.*.email
notation
π Performance optimized - Parallel validation and caching for high-performance applications
π Async rule support - Full support for asynchronous validation rules
π Comprehensive error reporting - Detailed field-specific error messages with path information
π¨ Type-safe - Built with Dart's strong type system for reliability
π Only validated data - Returns only the fields that passed validation, stripping unvalidated data
π Debug logging - Comprehensive logging for debugging validation process and performance analysis
Installation #
Add this package to your pubspec.yaml
:
dependencies:
input_validate: ^1.0.0
Then run:
dart pub get
Quick Start #
Basic Validation #
import 'package:input_validate/input_validate.dart';
// β¨ NEW: Concise syntax (recommended)
final rules = {
'name': [required(), string()],
'age': [required(), number(), min(18)],
'email': [required(), email()],
};
// π Or use verbose syntax (backward compatible)
final verboseRules = {
'name': [RequiredRule(), IsStringRule()],
'age': [RequiredRule(), IsNumberRule(), MinRule(18)],
'email': [RequiredRule(), EmailRule()],
};
final input = {
'name': 'John Doe',
'age': 25,
'email': 'john@example.com',
'password': 'secret', // This will be stripped from output
};
try {
final validated = await InputValidate.validate(input, rules);
print(validated);
// Output: {name: John Doe, age: 25, email: john@example.com}
// Notice: password field is stripped because it wasn't validated
} catch (e) {
if (e is MultipleValidationException) {
print('Validation errors: ${e.inputErrors}');
}
}
Nested Object Validation #
// Concise syntax
final rules = {
'user.name': [required(), string()],
'user.profile.age': [required(), number(), min(18)],
'settings.theme': [required(), inSet({'light', 'dark'})],
};
final input = {
'user': {
'name': 'Alice',
'profile': {'age': 28},
},
'settings': {'theme': 'dark'},
};
final validated = await InputValidate.validate(input, rules);
// Returns only the validated nested structure
Array Validation with Wildcards #
// Concise syntax for array validation
final rules = {
'users.*.name': [required(), string()],
'users.*.email': [required(), email()],
'users.*.roles': [required(), list()],
};
final input = {
'users': [
{
'name': 'Bob',
'email': 'bob@example.com',
'roles': ['admin'],
'password': 'secret', // Will be stripped
},
{
'name': 'Carol',
'email': 'carol@example.com',
'roles': ['user'],
},
],
};
final validated = await InputValidate.validate(input, rules);
// Returns users array with only validated fields
Nested Wildcard Validation #
final rules = {
'departments.*.name': [RequiredRule(), IsStringRule()],
'departments.*.employees.*.name': [RequiredRule(), IsStringRule()],
'departments.*.employees.*.salary': [RequiredRule(), IsNumberRule()],
};
// Validates deeply nested arrays and objects
Validation Syntax Options #
InputValidate offers two syntax styles for defining validation rules:
π Concise Syntax (Recommended) #
The new concise syntax provides cleaner, more readable validation rules:
final rules = {
'name': [required(), string(), min(2), max(50)],
'age': [required(), number(), min(18), max(120)],
'email': [required(), string(), email()],
'status': [required(), string(), inSet({'active', 'inactive'})],
'tags': [required(), list(), min(1), max(5)],
'profile.bio': [nullable(), string(), max(500)],
};
Available shorthand functions:
- Type validation:
required()
,string()
,number()
,boolean()
,list()
,map()
,email()
,nullable()
- Constraints:
min(value)
,max(value)
,inSet(values)
- Aliases:
isString()
,isNumber()
,isBoolean()
,isList()
,isMap()
,allowedValues(values)
π Verbose Syntax (Backward Compatible) #
The original class-based syntax remains fully supported:
final rules = {
'name': [RequiredRule(), IsStringRule(), MinRule(2), MaxRule(50)],
'age': [RequiredRule(), IsNumberRule(), MinRule(18), MaxRule(120)],
'email': [RequiredRule(), IsStringRule(), EmailRule()],
'status': [RequiredRule(), IsStringRule(), InRule({'active', 'inactive'})],
'tags': [RequiredRule(), IsListRule(), MinRule(1), MaxRule(5)],
'profile.bio': [NullableRule(), IsStringRule(), MaxRule(500)],
};
Mixing both syntaxes is supported:
final rules = {
'name': [required(), IsStringRule()], // Mixed syntax works fine
'age': [RequiredRule(), number(), min(18)], // Any combination
};
Available Validation Rules #
Type Rules #
RequiredRule()
/required()
- Field must be present and not null/emptyIsStringRule()
/string()
- Value must be a stringIsNumberRule()
/number()
- Value must be a number (int or double)IsBooleanRule()
/boolean()
- Value must be a booleanIsListRule()
/list()
- Value must be a listIsMapRule()
/map()
- Value must be a map
Constraint Rules #
MinRule(value)
/min(value)
- Minimum value for numbers or minimum length for strings/listsMaxRule(value)
/max(value)
- Maximum value for numbers or maximum length for strings/listsInRule(allowedValues)
/inSet(allowedValues)
- Value must be in the allowed set
Format Rules #
EmailRule()
/email()
- Value must be a valid email address
Special Rules #
NullableRule()
/nullable()
- Allows null values and skips other validation rules when value is null
Working with Nullable Fields #
The NullableRule
provides special behavior for handling optional fields. When a field value is null
and NullableRule
is present in the validation rules, all other validation rules for that field are skipped.
final rules = {
// Required field - null not allowed
'user.name': [RequiredRule(), IsStringRule()],
// Optional field - can be null or valid string
'user.email': [
NullableRule(), // Allow null values
IsStringRule(), // Only applied if value is not null
EmailRule(), // Only applied if value is not null
],
// Optional field with constraints
'user.bio': [
NullableRule(), // Allow null values
IsStringRule(), // Only applied if value is not null
MinRule(10), // Only applied if value is not null
MaxRule(500), // Only applied if value is not null
],
};
final input = {
'user': {
'name': 'John Doe',
'email': null, // β
Passes validation (NullableRule allows null)
'bio': null, // β
Passes validation (NullableRule allows null)
}
};
final result = await InputValidate.validate(input, rules);
// Returns: {'user': {'name': 'John Doe', 'email': null, 'bio': null}}
Important: If both RequiredRule
and NullableRule
are present, RequiredRule
takes precedence and null values will fail validation.
final rules = {
'field': [RequiredRule(), NullableRule(), IsStringRule()], // β RequiredRule prevents null
};
Performance Optimization #
Parallel Validation (Default) #
// Runs field validations in parallel for better performance
final result = await InputValidate.validate(
input,
rules,
enableParallelValidation: true, // Default
);
Sequential Validation with Early Termination #
// Validates fields sequentially with early termination on RequiredRule failures
final result = await InputValidate.validate(
input,
rules,
enableParallelValidation: false,
);
Field Path Caching #
The package automatically caches parsed field paths for improved performance on repeated validations.
Debug Logging #
The package includes comprehensive debug logging to help you understand the validation process and troubleshoot issues. All logs use Dart's built-in log()
function from dart:developer
.
Viewing Debug Logs #
In development, you can view debug logs.
Error Handling #
try {
final validated = await InputValidate.validate(input, rules);
} catch (e) {
if (e is MultipleValidationException) {
print('Failed fields: ${e.failureCount}');
// Access detailed error information
for (final entry in e.inputErrors!.entries) {
final fieldPath = entry.key;
final errors = entry.value;
print('$fieldPath: ${errors.join(', ')}');
}
}
}
Example Error Output #
name: This field is required
email: This field must be a valid email address
users.1.age: This field must be at least 18
users.2.email: This field must be a valid email address
Creating Custom Rules #
class CustomPasswordRule implements ValidationRule {
@override
String get message => 'Password must contain uppercase, lowercase, number, and special character.';
@override
FutureOr<bool> passes(dynamic value) {
if (value is! String || value.length < 8) return false;
return RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]').hasMatch(value);
}
}
// Usage
final rules = {
'password': [RequiredRule(), CustomPasswordRule()],
};
Key Benefits #
π― Data Security - Only validated fields are returned, preventing data leaks
β‘ Performance - Parallel validation and intelligent caching
π Debugging - Detailed error messages with exact field paths
π‘οΈ Type Safety - Leverages Dart's type system for reliability
π Scalability - Efficient handling of large, complex data structures
Wildcard Path Patterns #
Pattern | Description | Example |
---|---|---|
field |
Simple field | name , email |
object.field |
Nested field | user.name , settings.theme |
array.*.field |
Array elements | users.*.email |
object.array.*.field |
Nested array | dept.users.*.name |
array.*.object.field |
Array of objects | users.*.profile.age |
array.*.nested.*.field |
Deeply nested | groups.*.users.*.email |
π€ Author #
Firuz Vorisov
github.com/vfiruz97
Feel free to open issues or contribute via PR!
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License #
This project is licensed under the MIT License.