Skip to main content

Events and Callbacks

FDC exposes callbacks at several layers. The most maintainable applications place behavior at the layer that owns the state transition.

DataSet lifecycle events

For the complete callback catalog and exact execution order, see Lifecycle Callbacks and Events.

Use dataset events for data lifecycle policy:

  • open and close lifecycle;
  • edit, insert, post, delete, and cancel lifecycle;
  • state changes;
  • validation and dataset errors;
  • background work start, completion, and failure;
  • field changes;
  • record navigation.

For example:

final customers = FdcDataSet(
fields: fields,
beforePost: (dataSet) {
final creditLimit = dataSet.fieldByName('credit_limit').value;
if (creditLimit == null) {
throw FdcDataSetAbortException('Credit limit is required.');
}
},
afterPost: (dataSet) {
auditCustomerChange(dataSet);
},
onError: (dataSet, errors, cause) {
reportDataErrors(errors, cause: cause);
},
);

Use a dataset lifecycle callback when the rule must apply regardless of whether the change came from a grid, a standalone editor, or application code.

Grid callbacks

Use grid callbacks for interaction and presentation behavior:

  • cell changes;
  • row, column, and cell enter/exit notifications;
  • edit permission gates;
  • column value-changing/value-changed hooks;
  • lookup interactions;
  • context menus.

See Grid Events and Callbacks for grid-specific examples.

Editor callbacks

Standalone editors participate in the same dataset edit buffer. Use editor callbacks for UI-local behavior, while field and record validation stays at the field or dataset layer.

Lookup callbacks can return a value or a multi-field write set. See Lookup.

Shared field event model

Standalone editors and grid cell editors share a common field-event context model. This lets application logic inspect the same core information regardless of the UI host.

The event context identifies the host:

if (context.host == FdcFieldEventHost.grid) {
// Grid-specific coordinates may be available.
}

Common context information includes:

  • dataset and field name,
  • resolved field metadata,
  • typed logical values,
  • raw/unformatted values,
  • grid row and column coordinates when applicable,
  • host-specific row/column objects,
  • related-field value access.
onValueChanging: (context) {
final countryCode = context.valueOf<String>('country_code');
final stateCode = context.tryValueOf<String>('state_code');

return const FdcFieldValueChangeResult.accept();
}

valueOf requires the field to exist and preserves the typed contract. tryValueOf returns null when the field is missing or the runtime value does not match the requested type.

Accept, replace, or cancel a proposed value

onValueChanging can return one of three decisions.

Accept the proposed value:

return const FdcFieldValueChangeResult.accept();

Replace it with a normalized value:

return FdcFieldValueChangeResult.replaceValue(
normalizedCode,
);

Cancel the change, optionally with user-facing text:

return const FdcFieldValueChangeResult.cancel(
'Customer code is not valid.',
);

Returning null also accepts the proposed value.

Use setValueOf inside onValueChanging to stage related field updates that belong to the same logical edit operation:

onValueChanging: (context) {
final result = resolveState(context.newValue);

context.setValueOf('state_name', result.name);
context.setValueOf('region_code', result.regionCode);

return FdcFieldValueChangeResult.replaceValue(
result.normalizedCode,
);
}

The primary replacement and staged related values are applied together after the callback accepts the change. This is useful for code/name pairs, lookup-derived values, normalization, and dependent field synchronization.

Focus change reasons

Field focus contexts distinguish why focus changed:

  • mouse,
  • keyboard,
  • row indicator navigation,
  • dataset scroll,
  • edit commit,
  • edit cancel,
  • programmatic focus change,
  • focus traversal.

This allows UI-specific reactions without guessing from raw Flutter focus state.

Adapter callbacks and transport hooks

Adapter and transport callbacks belong to persistence boundaries. Examples include JSON/REST request transport, metadata builders, and custom adapter load/apply implementations.

Do not use a grid callback to implement adapter behavior, or an adapter hook to implement visual interaction policy.

Abort versus error

Throw FdcDataSetAbortException from supported veto points when an operation should be intentionally stopped. Use normal exceptions for actual failures. Silent aborts are available when the operation should stop without raising the normal dataset error event.