Configuration
Ack allows for some configuration settings, although most behavior is controlled directly through the schema definitions when they are created.
Global Settings (Less Common)
In general, Ack prefers configuration per schema definition rather than global settings. However, if needed, you might manage global aspects like custom format registries or shared validation logic through your own application structure (e.g., dependency injection or service patterns).
Currently, Ack does not expose a dedicated global configuration object. Configuration is primarily done at the schema level.
Schema-Level Configuration
Most "configuration" happens when you define your schemas using the fluent API provided by Ack.
Required Fields
Use the required
parameter in Ack.object
to specify mandatory fields within an object.
Ack.object({
'id': Ack.integer(),
'name': Ack.string(),
'email': Ack.string().email().optional() // Email is optional (can be omitted)
}); // id and name are required by default
Additional Properties
Control how extra fields (not defined in the schema) are handled using the additionalProperties
parameter in Ack.object
.
// Disallow any extra properties (default behavior)
Ack.object({
'id': Ack.integer()
}, additionalProperties: false);
// or simply:
Ack.object({
'id': Ack.integer()
});
// Allow any extra properties
Ack.object({
'id': Ack.integer()
}, additionalProperties: true);
Custom Error Messages
Built-in validation rules provide default error messages. For custom error messages, use custom constraints with the .constrain()
method.
See also: Custom Error Messages in Error Handling.
// Built-in constraints use default messages
Ack.string().minLength(5); // Default: "Too short, min 5 characters"
Ack.integer().min(18); // Default: "Must be at least 18"
// For custom messages, use custom constraints (see Custom Validation section below)
Custom Validation Logic
Use .constrain()
to add reusable validation logic for value-level checks, or .refine()
for cross-field rules.
import 'package:ack/ack.dart';
class IsPositiveConstraint extends Constraint<double> with Validator<double> {
IsPositiveConstraint()
: super(
constraintKey: 'is_positive',
description: 'Number must be positive',
);
@override
bool isValid(double value) => value > 0;
@override
String buildMessage(double value) => 'Number must be positive';
}
final priceSchema = Ack.double().constrain(IsPositiveConstraint());
// Cross-field validation (e.g., password confirmation)
final signUpSchema = Ack.object({
'password': Ack.string().minLength(8),
'confirmPassword': Ack.string().minLength(8),
}).refine(
(data) => data['password'] == data['confirmPassword'],
message: 'Passwords do not match',
);
See the Custom Validation Guide for additional patterns.
Code Generation Configuration
The ack_generator
package provides automatic schema generation from annotated classes. This feature is production-ready and available at version 1.0.0-beta.1 and later.
To use code generation, annotate your classes with @AckModel
. The generator creates both a validation schema and a type-safe extension type. Use @AckField
for field-level constraints:
import 'package:ack_annotations/ack_annotations.dart';
part 'user.g.dart';
@AckModel(
description: 'User profile with flexible preferences',
additionalProperties: true,
additionalPropertiesField: 'preferences'
)
class User {
@AckField(constraints: ['minLength(2)', 'maxLength(50)'])
final String name;
@AckField(constraints: ['email'])
final String email;
@AckField(constraints: ['min(18)'])
final int? age; // Optional field
// Collects extra properties not defined in the schema
final Map<String, dynamic> preferences;
User({
required this.name,
required this.email,
this.age,
this.preferences = const {},
});
}
After running dart run build_runner build
, the generator creates in user.g.dart
:
- Schema constant:
final userSchema = Ack.object({...});
- Extension type:
extension type UserType(Map<String, Object?> _data) {...}
// Generated in user.g.dart:
//
// final userSchema = Ack.object({
// 'name': Ack.string().minLength(2).maxLength(50),
// 'email': Ack.string().email(),
// 'age': Ack.integer().min(18).optional().nullable(),
// }, additionalProperties: true);
//
// extension type UserType(Map<String, Object?> _data) {
// static UserType parse(Object? data) { ... }
// static SchemaResult<UserType> safeParse(Object? data) { ... }
// String get name => ...
// String get email => ...
// int? get age => ...
// Map<String, dynamic> get preferences => ...
// }
// Usage with the generated extension type:
final userData = {
'name': 'John Doe',
'email': 'john@example.com',
'age': 25,
'theme': 'dark',
'language': 'en',
'notifications': true,
};
final result = UserType.safeParse(userData);
if (result.isOk) {
final user = result.getOrThrow();
print(user.name); // Type-safe: John Doe
print(user.email); // Type-safe: john@example.com
print(user.preferences['theme']); // dark (additional properties)
}
See the Installation Guide for setup instructions and the generator README for advanced features.
Summary
- Ack primarily uses schema-level configuration through methods and parameters like
.minLength()
,.optional()
,additionalProperties: ...
. - Manual schema definition is the recommended approach for production applications.
- There is currently no central global configuration object provided by Ack itself.