Skip to main content

Validation

Validation is part of the dataset post lifecycle. A record is validated before posted values become committed dataset state.

Required fields

The simplest rule belongs in schema metadata:

FdcStringField(
name: 'name',
label: 'Name',
required: true,
)

Required rules are enforced by built-in dataset validation.

Field-level validation

Use a field validator when a rule belongs to one field but may still need row context:

FdcStringField(
name: 'code',
label: 'Code',
validator: (row, value) {
final text = value as String?;
if (text != null && text.length < 3) {
return 'Code must contain at least 3 characters.';
}
return null;
},
)

Return null when the value is valid, or a user-facing message when it is invalid.

Record-level validation

Cross-field business rules belong in recordValidator:

final dataSet = FdcDataSet(
fields: fields,
recordValidator: (row) {
final start = row['start_date'] as DateTime?;
final end = row['end_date'] as DateTime?;

if (start != null && end != null && end.isBefore(start)) {
return [
const FdcValidationError(
message: 'End date must not be before start date.',
fieldName: 'end_date',
),
];
}

return const [];
},
);

A record validator returns zero or more FdcValidationError objects and can target a specific field or the record as a whole.

React to validation failures

Use onValidationError when the application needs to react to validation failures:

final dataSet = FdcDataSet(
fields: fields,
onValidationError: (dataSet, errors) {
// Display or route validation feedback as needed.
},
);

The dataset remains the validation authority whether edits originate from code, a data-aware editor, or the grid.

Keep rules near their owner

A practical rule of thumb:

  • schema constraints such as required values belong on field definitions;
  • one-field custom rules belong in the field validator;
  • cross-field and whole-record rules belong in recordValidator.

This keeps validation reusable across every UI bound to the dataset.