Primitives
String
Description: Validates that the input is a String. Optionally enforces length bounds, format rules, and pattern matching.
Signature:
ZemaString string()
Validation order: type check → trim (if enabled) → length → exact length → pattern → email/url/uuid → enum.
All active constraints are evaluated. All failures are returned together.
Constraints
.min(n, {String? message})
Requires the string to have at least n characters. Produces too_short.
z.string().min(2)
z.string().min(8, message: 'Password must be at least 8 characters.')
.max(n, {String? message})
Requires the string to have at most n characters. Produces too_long.
z.string().max(100)
z.string().max(280, message: 'Exceeds 280 characters.')
.length(n, {String? message})
Requires the string to have exactly n characters. Produces wrong_length.
z.string().length(5) // exactly 5 chars
z.string().length(10, message: 'Must be exactly 10 digits.')
.regex(RegExp re, {String? message})
Requires the string to fully match re. Produces invalid_format.
z.string().regex(RegExp(r'^\d{5}$')) // 5-digit postal code
z.string().regex(
RegExp(r'^[a-zA-Z0-9_]+$'),
message: 'Only letters, numbers, and underscores.',
)
.email({String? message})
Requires a syntactically valid email address (RFC-aligned regex, no DNS resolution). Produces invalid_email.
z.string().email()
z.string().email(message: 'Enter a valid email.')
.url()
Requires a valid http or https URL. Produces invalid_url.
z.string().url()
.uuid()
Requires a valid UUID v4 (case-insensitive). Produces invalid_uuid.
z.string().uuid()
.dateTime({String? message})
Requires the string to be a valid ISO 8601 date-time parseable by DateTime.tryParse. Validates format only, the output type remains String. Produces invalid_datetime_string on failure.
z.string().dateTime() // validates ISO 8601 format
z.string().dateTime().transform(DateTime.parse) // → DateTime output
For inputs that may be DateTime, ISO 8601 String, or Unix int, use z.dateTime() or z.coerce().dateTime() instead.
.oneOf(List<String> values, {String? message})
Requires the string to equal one of the provided values. Produces invalid_enum.
z.string().oneOf(['admin', 'editor', 'viewer'])
.trim()
Strips leading and trailing whitespace before all other checks. The trimmed value is returned on success.
z.string().trim().min(1) // rejects blank strings
Error codes
| Code | Trigger |
|---|---|
invalid_type | Input is not a String. |
too_short | Length below .min(). |
too_long | Length above .max(). |
wrong_length | Length differs from .length(). |
invalid_format | Fails .regex(). |
invalid_email | Fails .email(). |
invalid_url | Fails .url(). |
invalid_uuid | Fails .uuid(). |
invalid_datetime_string | Fails .dateTime(). |
invalid_enum | Not in .oneOf(). |
Examples
// Username
final username = z.string()
.trim()
.min(3)
.max(20)
.regex(RegExp(r'^[a-zA-Z0-9_]+$'), message: 'Alphanumeric and underscores only.');
// Password
final password = z.string()
.min(8, message: 'At least 8 characters.')
.regex(RegExp(r'(?=.*[0-9])'), message: 'At least one digit.');
// Postal code (exactly 5 digits)
final postalCode = z.string().length(5).regex(RegExp(r'^\d{5}$'));
Integer
Description: Validates that the input is a Dart int. Rejects double (even 42.0) and numeric strings.
Signature:
ZemaInt integer()
For coercion from strings or doubles: z.coerce().integer().
Constraints
.gte(n, {String? message})
Requires value >= n (inclusive). Produces too_small.
z.integer().gte(0) // non-negative
z.integer().gte(18) // minimum age
.lte(n, {String? message})
Requires value <= n (inclusive). Produces too_big.
z.integer().lte(100)
z.integer().gte(0).lte(255) // byte range
.gt(n, {String? message})
Requires value > n (exclusive). Produces too_small_exclusive.
z.integer().gt(0) // 1, 2, 3, … (0 rejected)
z.integer().gt(18) // strictly older than 18
.lt(n, {String? message})
Requires value < n (exclusive). Produces too_big_exclusive.
z.integer().lt(100) // …, 98, 99 (100 rejected)
z.integer().lt(0) // strictly negative
.positive({String? message})
Requires value > 0. Produces not_positive. Zero is rejected.
z.integer().positive() // 1, 2, 3, …
.negative({String? message})
Requires value < 0. Produces not_negative. Zero is rejected.
z.integer().negative() // …, -2, -1
.step(n, {String? message})
Requires value % n == 0. Produces not_multiple_of.
z.integer().step(5) // 0, 5, 10, 15, …
z.integer().step(2) // even numbers
Inclusive vs exclusive bounds
| Method | Condition | Bound included |
|---|---|---|
.gte(n) | value >= n | Yes |
.lte(n) | value <= n | Yes |
.gt(n) | value > n | No |
.lt(n) | value < n | No |
z.integer().gte(1).lte(10) // [1, 10] — closed interval
z.integer().gt(0).lt(11) // [1, 10] — same range, open bounds
Error codes
| Code | Trigger |
|---|---|
invalid_type | Input is not an int. |
too_small | Below .gte(). |
too_big | Above .lte(). |
too_small_exclusive | At or below .gt(). |
too_big_exclusive | At or above .lt(). |
not_positive | Zero or negative, fails .positive(). |
not_negative | Zero or positive, fails .negative(). |
not_multiple_of | Not divisible by .step(). |
Examples
// Age (18+)
final age = z.integer().gte(18).lte(120);
// Page number (strictly positive)
final page = z.integer().positive();
// HTTP port (1–65535)
final port = z.integer().gte(1).lte(65535);
// Even positive numbers
final evenPositive = z.integer().positive().step(2);
Double
Description: Validates that the input is a Dart double. Rejects int values and numeric strings.
Signature:
ZemaDouble double()
For coercion from strings or integers: z.coerce().float().
Constraints
.gte(n, {String? message})
Requires value >= n. Produces too_small.
z.double().gte(0.0) // non-negative
.lte(n, {String? message})
Requires value <= n. Produces too_big.
z.double().gte(0.0).lte(1.0) // probability range [0.0, 1.0]
.gt(n, {String? message})
Requires value > n (exclusive). Produces too_small_exclusive.
z.double().gt(0.0) // strictly positive
z.double().gt(0.0).lt(1.0) // open interval (0.0, 1.0)
.lt(n, {String? message})
Requires value < n (exclusive). Produces too_big_exclusive.
z.double().lt(1.0)
z.double().gt(0.0).lt(1.0) // open interval (0.0, 1.0)
.positive()
Requires value > 0.0. Produces not_positive. Zero is rejected.
z.double().positive()
.negative({String? message})
Requires value < 0.0. Produces not_negative. Zero is rejected.
z.double().negative()
z.double().negative(message: 'Must be a loss value.')
.finite()
Requires the value to not be NaN, Infinity, or -Infinity. Produces not_finite. Runs before range constraints.
z.double().finite()
z.double().finite().gte(0.0)
Inclusive vs exclusive bounds
| Method | Condition | Bound included |
|---|---|---|
.gte(n) | value >= n | Yes |
.lte(n) | value <= n | Yes |
.gt(n) | value > n | No |
.lt(n) | value < n | No |
Error codes
| Code | Trigger |
|---|---|
invalid_type | Input is not a double. |
too_small | Below .gte(). |
too_big | Above .lte(). |
too_small_exclusive | At or below .gt(). |
too_big_exclusive | At or above .lt(). |
not_positive | Zero or negative, fails .positive(). |
not_negative | Zero or positive, fails .negative(). |
not_finite | NaN or infinite, fails .finite(). |
Examples
// Probability
final probability = z.double().gte(0.0).lte(1.0);
// Open interval
final openUnit = z.double().gt(0.0).lt(1.0);
// Price
final price = z.double().finite().positive();
// Temperature delta (can be negative, must be finite)
final delta = z.double().finite();
Boolean
Description: Validates that the input is a Dart bool. Accepts only true or false. Rejects 1, 0, and strings.
Signature:
ZemaBool boolean()
For coercion from strings and integers: z.coerce().boolean().
z.boolean().parse(true); // true
z.boolean().parse(false); // false
z.boolean().parse(1); // ZemaException: invalid_type
z.boolean().parse('true'); // ZemaException: invalid_type
Error codes
| Code | Trigger |
|---|---|
invalid_type | Input is not a bool. |
DateTime
Description: Validates and coerces date/time values into DateTime. Accepts three input representations.
Signature:
ZemaDateTime dateTime()
| Input type | Behaviour |
|---|---|
DateTime | Passed through unchanged. |
String | Parsed with DateTime.tryParse (ISO 8601). |
int | Interpreted as milliseconds since the Unix epoch. |
| Anything else | invalid_date failure. |
final schema = z.dateTime();
schema.parse(DateTime(2024, 1, 15)); // DateTime
schema.parse('2024-01-15T10:30:00Z'); // ISO 8601 string
schema.parse(1705312200000); // Unix ms timestamp
schema.parse('not-a-date'); // ZemaException: invalid_date
Constraints
.after(DateTime date)
Requires the parsed date to be on or after date. Produces date_too_early.
z.dateTime().after(DateTime(2000))
.before(DateTime date)
Requires the parsed date to be on or before date. Produces date_too_late.
z.dateTime().before(DateTime.now()) // must be in the past
.between(DateTime start, DateTime end)
Requires the parsed date to fall within [start, end] (inclusive). Equivalent to .after(start).before(end).
z.dateTime().between(
DateTime(2024, 1, 1),
DateTime(2024, 12, 31),
)
Error codes
| Code | Trigger |
|---|---|
invalid_date | Input cannot be parsed as a date. |
date_too_early | Before .after() bound. |
date_too_late | After .before() bound. |
Coercion
All four primitive types have a coercion variant accessible via z.coerce().
z.coerce().integer() // '42' → 42, 42.0 → 42
z.coerce().float() // '3.14' → 3.14, 3 → 3.0
z.coerce().boolean() // 'true' | 1 | 'yes' → true
z.coerce().string() // 42 → '42'
Coercion schemas return invalid_coercion on inputs that cannot be converted.
Modifiers (all schemas)
Every primitive schema inherits these modifiers from ZemaSchema:
z.string().optional() // null input → null output
z.string().nullable() // null input → null output (explicit nullability)
z.string().withDefault('anon') // null input → 'anon'
z.integer().catchError((_) => 0) // any failure → 0