Map Validation Examples
Learn how to validate maps and objects with Validasi's map validation rules.
Basic Map Validation
Simple Object Validation
dart
import 'package:validasi/validasi.dart';
import 'package:validasi/rules.dart';
final userSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'name': Validasi.string([
StringRules.minLength(1),
]),
'age': Validasi.number<int>([
NumberRules.moreThanEqual(0),
]),
}),
]);
// Valid
final validUser = {'name': 'John', 'age': 30};
print(userSchema.validate(validUser).isValid); // true
// Invalid
final invalidUser = {'name': '', 'age': -1};
final result = userSchema.validate(invalidUser);
print(result.isValid); // false
for (var error in result.errors) {
print('[${error.path?.join('.')}] ${error.message}');
}Required Keys Validation
dart
final configSchema = Validasi.map<dynamic>([
MapRules.hasFieldKeys({'api_key', 'endpoint', 'timeout'}),
]);
// Valid
print(configSchema.validate({
'api_key': 'abc123',
'endpoint': 'https://api.example.com',
'timeout': 30,
'extra': 'allowed', // Extra fields are OK
}).isValid); // true
// Invalid - missing keys
print(configSchema.validate({
'api_key': 'abc123',
}).isValid); // falseNested Map Validation
One Level Nesting
dart
final profileSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'user': Validasi.map<dynamic>([
MapRules.hasFields({
'name': Validasi.string([StringRules.minLength(1)]),
'email': Validasi.string([StringRules.minLength(5)]),
}),
]),
'settings': Validasi.map<dynamic>([
MapRules.hasFields({
'theme': Validasi.string([
StringRules.oneOf(['light', 'dark', 'auto']),
]),
'notifications': Validasi.any<bool>(),
}),
]),
}),
]);
final profile = {
'user': {
'name': 'John Doe',
'email': 'john@example.com',
},
'settings': {
'theme': 'dark',
'notifications': true,
},
};
print(profileSchema.validate(profile).isValid); // trueDeep Nesting
dart
final addressSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'street': Validasi.string([StringRules.minLength(1)]),
'city': Validasi.string([StringRules.minLength(1)]),
'country': Validasi.string([StringRules.minLength(1)]),
'coordinates': Validasi.map<dynamic>([
MapRules.hasFields({
'lat': Validasi.number<double>([
NumberRules.moreThanEqual(-90.0),
NumberRules.lessThanEqual(90.0),
]),
'lng': Validasi.number<double>([
NumberRules.moreThanEqual(-180.0),
NumberRules.lessThanEqual(180.0),
]),
}),
]),
}),
]);
final address = {
'street': '123 Main St',
'city': 'San Francisco',
'country': 'USA',
'coordinates': {
'lat': 37.7749,
'lng': -122.4194,
},
};
print(addressSchema.validate(address).isValid); // trueConditional Field Validation
Basic Conditional Validation
dart
final orderSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'is_delivery': Validasi.any<bool>(),
'address': Validasi.string([
Nullable(),
StringRules.minLength(10),
]),
}),
MapRules.conditionalField('address', (context, value) {
if (context.get('is_delivery') == true && value == null) {
return 'Address is required for delivery orders';
}
return null;
}),
]);
// Valid - delivery with address
print(orderSchema.validate({
'is_delivery': true,
'address': '123 Main Street, City',
}).isValid); // true
// Valid - pickup without address
print(orderSchema.validate({
'is_delivery': false,
'address': null,
}).isValid); // true
// Invalid - delivery without address
print(orderSchema.validate({
'is_delivery': true,
'address': null,
}).isValid); // falseMultiple Conditional Fields
dart
final paymentSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'payment_method': Validasi.string([
StringRules.oneOf(['card', 'bank', 'cash']),
]),
'card_number': Validasi.string([Nullable()]),
'bank_account': Validasi.string([Nullable()]),
}),
MapRules.conditionalField('card_number', (context, value) {
if (context.get('payment_method') == 'card' && value == null) {
return 'Card number is required for card payments';
}
return null;
}),
MapRules.conditionalField('bank_account', (context, value) {
if (context.get('payment_method') == 'bank' && value == null) {
return 'Bank account is required for bank transfers';
}
return null;
}),
]);
// Valid - card payment with card number
print(paymentSchema.validate({
'payment_method': 'card',
'card_number': '4111111111111111',
'bank_account': null,
}).isValid); // true
// Valid - cash payment
print(paymentSchema.validate({
'payment_method': 'cash',
'card_number': null,
'bank_account': null,
}).isValid); // true
// Invalid - card payment without card number
print(paymentSchema.validate({
'payment_method': 'card',
'card_number': null,
'bank_account': null,
}).isValid); // falseOptional Fields
Nullable Fields
dart
final userSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'name': Validasi.string([StringRules.minLength(1)]),
'nickname': Validasi.string([
Nullable(),
StringRules.minLength(2),
]),
'age': Validasi.number<int>([
Nullable(),
NumberRules.moreThanEqual(0),
]),
}),
]);
// Valid with nulls
print(userSchema.validate({
'name': 'John Doe',
'nickname': null,
'age': null,
}).isValid); // true
// Valid with values
print(userSchema.validate({
'name': 'John Doe',
'nickname': 'JD',
'age': 30,
}).isValid); // trueMap Transformations
Transform Field Values
dart
final userSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'email': Validasi.string([
Transform((value) => value?.trim().toLowerCase()),
StringRules.minLength(5),
]),
'name': Validasi.string([
Transform((value) => value?.trim()),
Transform((value) {
// Capitalize first letter of each word
return value?.split(' ')
.map((word) => word.isEmpty ? '' :
word[0].toUpperCase() + word.substring(1).toLowerCase())
.join(' ');
}),
]),
}),
]);
final result = userSchema.validate({
'email': ' USER@EXAMPLE.COM ',
'name': 'john doe',
});
print(result.data);
// {
// email: 'user@example.com',
// name: 'John Doe'
// }Real-World Examples
Registration Form
dart
final registrationSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'username': Validasi.string([
Transform((value) => value?.trim().toLowerCase()),
StringRules.minLength(3),
StringRules.maxLength(20),
InlineRule<String>((value) {
if (!RegExp(r'^[a-z0-9_]+$').hasMatch(value)) {
return 'Username can only contain letters, numbers, and underscores';
}
return null;
}),
]),
'email': Validasi.string([
Transform((value) => value?.trim().toLowerCase()),
StringRules.minLength(5),
InlineRule<String>((value) {
if (!value.contains('@')) {
return 'Invalid email format';
}
return null;
}),
]),
'password': Validasi.string([
StringRules.minLength(8),
InlineRule<String>((value) {
if (!value.contains(RegExp(r'[A-Z]'))) {
return 'Password must contain uppercase letter';
}
if (!value.contains(RegExp(r'[0-9]'))) {
return 'Password must contain number';
}
return null;
}),
]),
'age': Validasi.number<int>([
Nullable(),
NumberRules.moreThanEqual(13),
]),
'terms': Validasi.any<bool>([
InlineRule<bool>((value) {
return value == true ? null : 'You must accept the terms';
}),
]),
}),
]);
final formData = {
'username': 'JohnDoe123',
'email': 'JOHN@EXAMPLE.COM',
'password': 'SecurePass123',
'age': 25,
'terms': true,
};
final result = registrationSchema.validate(formData);
print('Valid: ${result.isValid}');
print('Processed data: ${result.data}');API Request Validation
dart
final apiRequestSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'endpoint': Validasi.string([
StringRules.minLength(1),
]),
'method': Validasi.string([
StringRules.oneOf(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']),
]),
'headers': Validasi.map<dynamic>([
Nullable(),
]),
'body': Validasi.any<dynamic>([
Nullable(),
]),
'timeout': Validasi.number<int>([
Nullable(),
NumberRules.moreThan(0),
NumberRules.lessThanEqual(300),
]),
}),
]);
final request = {
'endpoint': '/api/users',
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123',
},
'body': {
'name': 'John Doe',
'email': 'john@example.com',
},
'timeout': 30,
};
print(apiRequestSchema.validate(request).isValid); // trueProduct Validation
dart
final productSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'name': Validasi.string([
StringRules.minLength(1),
StringRules.maxLength(100),
]),
'description': Validasi.string([
StringRules.minLength(10),
StringRules.maxLength(500),
]),
'price': Validasi.number<double>([
NumberRules.moreThan(0.0),
]),
'stock': Validasi.number<int>([
NumberRules.moreThanEqual(0),
]),
'category': Validasi.string([
StringRules.oneOf(['electronics', 'clothing', 'books', 'food']),
]),
'tags': Validasi.list<String>([
IterableRules.minLength(1),
IterableRules.forEach(
Validasi.string([StringRules.minLength(2)]),
),
]),
'on_sale': Validasi.any<bool>(),
'discount': Validasi.number<double>([
Nullable(),
NumberRules.moreThanEqual(0.0),
NumberRules.lessThan(100.0),
]),
}),
MapRules.conditionalField('discount', (context, value) {
if (context.get('on_sale') == true && value == null) {
return 'Discount is required when product is on sale';
}
return null;
}),
]);
final product = {
'name': 'Wireless Headphones',
'description': 'High-quality wireless headphones with noise cancellation',
'price': 99.99,
'stock': 50,
'category': 'electronics',
'tags': ['audio', 'wireless', 'bluetooth'],
'on_sale': true,
'discount': 15.0,
};
print(productSchema.validate(product).isValid); // trueBlog Post Validation
dart
final blogPostSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'title': Validasi.string([
StringRules.minLength(5),
StringRules.maxLength(200),
]),
'slug': Validasi.string([
Transform((value) => value?.trim().toLowerCase()),
Transform((value) => value?.replaceAll(RegExp(r'\s+'), '-')),
StringRules.minLength(5),
InlineRule<String>((value) {
if (!RegExp(r'^[a-z0-9-]+$').hasMatch(value)) {
return 'Slug can only contain lowercase letters, numbers, and hyphens';
}
return null;
}),
]),
'content': Validasi.string([
StringRules.minLength(100),
]),
'author': Validasi.map<dynamic>([
MapRules.hasFields({
'id': Validasi.number<int>([NumberRules.moreThan(0)]),
'name': Validasi.string([StringRules.minLength(1)]),
}),
]),
'tags': Validasi.list<String>([
IterableRules.minLength(1),
IterableRules.forEach(
Validasi.string([
Transform((value) => value?.trim().toLowerCase()),
StringRules.minLength(2),
]),
),
]),
'published': Validasi.any<bool>(),
'published_at': Validasi.string([
Nullable(),
]),
}),
MapRules.conditionalField('published_at', (context, value) {
if (context.get('published') == true && value == null) {
return 'Published date is required when post is published';
}
return null;
}),
]);
final blogPost = {
'title': 'Getting Started with Flutter',
'slug': 'Getting Started With Flutter',
'content': 'Flutter is an amazing framework...' + ('.' * 100),
'author': {
'id': 123,
'name': 'John Doe',
},
'tags': ['Flutter', 'Mobile', 'Development'],
'published': true,
'published_at': '2024-01-15T10:00:00Z',
};
final result = blogPostSchema.validate(blogPost);
print('Valid: ${result.isValid}');
print('Slug: ${result.data['slug']}'); // 'getting-started-with-flutter'Settings Validation
dart
final settingsSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'appearance': Validasi.map<dynamic>([
MapRules.hasFields({
'theme': Validasi.string([
StringRules.oneOf(['light', 'dark', 'auto']),
]),
'font_size': Validasi.number<int>([
NumberRules.moreThanEqual(8),
NumberRules.lessThanEqual(32),
]),
}),
]),
'notifications': Validasi.map<dynamic>([
MapRules.hasFields({
'email': Validasi.any<bool>(),
'push': Validasi.any<bool>(),
'sms': Validasi.any<bool>(),
}),
]),
'privacy': Validasi.map<dynamic>([
MapRules.hasFields({
'profile_visible': Validasi.any<bool>(),
'show_email': Validasi.any<bool>(),
}),
]),
}),
]);
final settings = {
'appearance': {
'theme': 'dark',
'font_size': 14,
},
'notifications': {
'email': true,
'push': true,
'sms': false,
},
'privacy': {
'profile_visible': true,
'show_email': false,
},
};
print(settingsSchema.validate(settings).isValid); // trueCustom Map Validation
Cross-Field Validation
dart
final passwordMatchSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'password': Validasi.string([StringRules.minLength(8)]),
'confirm_password': Validasi.string([StringRules.minLength(8)]),
}),
Having((context, value) {
if (value is Map && value['password'] != value['confirm_password']) {
return 'Passwords do not match';
}
return null;
}),
]);
// Valid
print(passwordMatchSchema.validate({
'password': 'SecurePass123',
'confirm_password': 'SecurePass123',
}).isValid); // true
// Invalid
print(passwordMatchSchema.validate({
'password': 'SecurePass123',
'confirm_password': 'DifferentPass',
}).isValid); // falseDate Range Validation
dart
final dateRangeSchema = Validasi.map<dynamic>([
MapRules.hasFields({
'start_date': Validasi.string([StringRules.minLength(1)]),
'end_date': Validasi.string([StringRules.minLength(1)]),
}),
Having((context, value) {
if (value is Map) {
final start = DateTime.tryParse(value['start_date'] ?? '');
final end = DateTime.tryParse(value['end_date'] ?? '');
if (start != null && end != null && end.isBefore(start)) {
return 'End date must be after start date';
}
}
return null;
}),
]);Error Handling in Maps
dart
final schema = Validasi.map<dynamic>([
MapRules.hasFields({
'user': Validasi.map<dynamic>([
MapRules.hasFields({
'name': Validasi.string([
StringRules.minLength(1, message: 'Name is required'),
]),
'age': Validasi.number<int>([
NumberRules.moreThanEqual(0, message: 'Age must be positive'),
]),
}),
]),
}),
]);
final result = schema.validate({
'user': {
'name': '',
'age': -5,
},
});
for (var error in result.errors) {
print('[${error.path?.join('.')}] ${error.message}');
}
// Output:
// [user.name] Name is required
// [user.age] Age must be positive