DataSet
FdcDataSet is the central object in the FD Components programming model. It owns a typed schema, records, cursor position, editing state, validation, change tracking, query state, and adapter interaction.
A grid or editor binds to the same dataset instead of maintaining a separate copy of business data. This gives application screens one consistent source of truth for navigation, editing, validation, and persistence.
Mental model
Think of a dataset as five cooperating layers:
- Schema — typed
FdcFieldDefdefinitions describe field names, types, labels, validation, defaults, keys, and persistence metadata. - Records — loaded rows are stored as dataset records and exposed through the active dataset view.
- Cursor — one visible record is current. Navigation changes that position and all bound controls follow it.
- Edit buffer — edits and inserts are staged through dataset state rather than written directly to accepted record values.
- Persistence boundary — local datasets can own rows directly; adapter-backed datasets load and apply data through an adapter.
Local and adapter-backed datasets
A local dataset owns rows directly:
final dataSet = FdcDataSet(
fields: const <FdcFieldDef>[
FdcIntegerField(name: 'id', isKey: true),
FdcStringField(name: 'name', required: true),
],
);
await dataSet.loadRows(<Map<String, Object?>>[
<String, Object?>{'id': 1, 'name': 'Northstar Analytics'},
]);
An adapter-backed dataset delegates loading and persistence:
final dataSet = FdcDataSet(
fields: customerFields,
adapter: adapter,
);
await dataSet.open();
The surrounding programming model remains consistent. Navigation, field access, editing, validation, grids, and editors continue to operate through the dataset.
The dataset owns lifecycle state
The main states are:
closed → loading → browse
↓
edit / insert
↓
browse
Adapter persistence may also enter applyingUpdates.
Use the state API when application behavior genuinely depends on lifecycle state:
if (dataSet.state == FdcDataSetState.edit) {
dataSet.post();
}
For common intent, prefer higher-level properties such as isOpen, isEmpty, and hasUpdates instead of building application logic around every state transition.
Navigation is record-oriented
The dataset exposes classic cursor navigation:
dataSet.first();
dataSet.next();
dataSet.prior();
dataSet.last();
dataSet.moveToRecord(5); // 1-based public record number
The current record position is available through recordNumber, while recordCount reports the number of visible records in the active view.
print('${dataSet.recordNumber} / ${dataSet.recordCount}');
Filtering, sorting, searching, paging, and master-detail constraints can change the visible view without changing the core dataset programming model.
Read and write fields through the dataset
The shortest value access form is:
final name = dataSet['name'];
dataSet.edit();
dataSet['name'] = 'BluePeak Logistics';
dataSet.post();
For typed reads and metadata, use fieldByName and fieldDef:
final name = dataSet.fieldByName('name').asString;
final amountField = dataSet.fieldDef<FdcDecimalField>('amount');
print(amountField.precision);
The distinction between schema metadata and runtime values is covered in Fields.
UI components share the same dataset
A grid can bind to the full dataset:
FdcGrid(
dataSet: dataSet,
columns: const <FdcGridColumn>[
FdcIntegerColumn(fieldName: 'id'),
FdcTextColumn(fieldName: 'name'),
],
)
A data-aware editor binds to one field:
FdcTextEdit(
dataSet: dataSet,
fieldName: 'name',
)
Both observe the same current record and editing lifecycle. A change made through one bound component becomes visible to the others through dataset notifications.
What the DataSet is not
FdcDataSet is not only a collection of maps and it is not only a grid data source. It is the data workflow layer connecting:
- typed field metadata;
- records and cursor navigation;
- editing and validation;
- change tracking;
- filtering, sorting, searching, and paging;
- grids and editors;
- data adapters and persistence.
That shared model is what allows FD Components screens to stay RAD-friendly as they grow beyond a single widget.
Next: Fields