Zema Logo
Schemas

Primitives

Reference for Zema's primitive schema types: string, integer, double, boolean, and dateTime.

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

CodeTrigger
invalid_typeInput is not a String.
too_shortLength below .min().
too_longLength above .max().
wrong_lengthLength differs from .length().
invalid_formatFails .regex().
invalid_emailFails .email().
invalid_urlFails .url().
invalid_uuidFails .uuid().
invalid_datetime_stringFails .dateTime().
invalid_enumNot 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

MethodConditionBound included
.gte(n)value >= nYes
.lte(n)value <= nYes
.gt(n)value > nNo
.lt(n)value < nNo
z.integer().gte(1).lte(10)   // [1, 10]  — closed interval
z.integer().gt(0).lt(11)     // [1, 10]  — same range, open bounds

Error codes

CodeTrigger
invalid_typeInput is not an int.
too_smallBelow .gte().
too_bigAbove .lte().
too_small_exclusiveAt or below .gt().
too_big_exclusiveAt or above .lt().
not_positiveZero or negative, fails .positive().
not_negativeZero or positive, fails .negative().
not_multiple_ofNot 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

MethodConditionBound included
.gte(n)value >= nYes
.lte(n)value <= nYes
.gt(n)value > nNo
.lt(n)value < nNo

Error codes

CodeTrigger
invalid_typeInput is not a double.
too_smallBelow .gte().
too_bigAbove .lte().
too_small_exclusiveAt or below .gt().
too_big_exclusiveAt or above .lt().
not_positiveZero or negative, fails .positive().
not_negativeZero or positive, fails .negative().
not_finiteNaN 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

CodeTrigger
invalid_typeInput is not a bool.

DateTime

Description: Validates and coerces date/time values into DateTime. Accepts three input representations.

Signature:

ZemaDateTime dateTime()
Input typeBehaviour
DateTimePassed through unchanged.
StringParsed with DateTime.tryParse (ISO 8601).
intInterpreted as milliseconds since the Unix epoch.
Anything elseinvalid_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

CodeTrigger
invalid_dateInput cannot be parsed as a date.
date_too_earlyBefore .after() bound.
date_too_lateAfter .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

Next Steps

Copyright © 2026