Skip to content

2. Fields, Types, and Rows

A FieldType describes value interpretation. A Field<T> is a named, reusable token that carries that type plus storage, UI, seed, defaulting, and reference metadata.

Prefer semantic field tokens

dart
abstract final class AreaFields {
  static const code = CodeField();
  static const name = NameField();
  static const description = DescriptionField();
  static const tenantId = TenantIdField();
  static const siteId = SiteIdField();
}

CodeField, NameField, and TenantIdField encode shared conventions. Use primitive tokens when the semantics are genuinely local:

dart
static const capacity = IntegerField(
  'capacity',
  title: 'Capacity',
  nullable: true,
);

static const roomNumber = TextField(
  'room_number',
  title: 'Room Number',
  maxLength: 80,
);

static const calibratedAt = TimestampField(
  'calibrated_at',
  title: 'Calibrated At',
  withTimeZone: true,
);

Closed field types

The sealed interpretation core covers text, integers, big integers, doubles, exact decimals, booleans, timestamps, dates, intervals, UUIDs, JSONB, enums, and decisions. Higher-level tokens such as quantities, money, files, identifiers, and related fields compose those primitives.

Use DecimalField for regulated or financial precision. Its Dart boundary is a string so a binary floating-point conversion cannot silently change it.

Enums keep wire values stable and labels human

dart
enum AreaType { room, suite, zone, corridor, airlock, wash_bay }

static const areaType = EnumField(
  'area_type',
  AreaType.values,
  dbEnumName: 'area_type',
  title: 'Area Type',
  ui: FieldUI(
    valueLabels: {'wash_bay': 'Wash Bay'},
  ),
);

The persisted value is wash_bay; the UI renders Wash Bay. Filters, tables, forms, chips, and details all consume the same label map.

Defaults belong at the correct layer

dart
static const id = UuidField(
  'id',
  nullable: false,
  defaultExpr: FieldDefaultExpr.uuidV4,
);

static const createdAt = TimestampField(
  'created_at',
  nullable: false,
  defaultExpr: FieldDefaultExpr.now,
);

static const createdBy = ActorField(
  'created_by',
  defaultExpr: FieldDefaultExpr.actorId,
);

Postgres can own uuidV4 and now. The authenticated runtime injects actorId and siteId; they cannot be honest SQL defaults.

Reference fields carry picker semantics

dart
static const areaCategoryId = UuidField(
  'area_category_id',
  title: 'Area Category',
  indexed: true,
  db: FieldDb(
    queryPatterns: [QueryPattern.filter, QueryPattern.aggregate],
  ),
  ui: FieldUI(
    widget: UIFieldControl.referencePicker,
    filterable: true,
  ),
  reference: UIReferenceHint(
    targetEntity: 'ops.area_category',
    projection: 'picker',
  ),
);

The storage value remains a UUID. The generated editor uses an entity picker and the generated detail surface uses an entity link. A user should not have to type or interpret the raw ID.

Typed row access

The same token reads and writes a loose storage row safely:

dart
final row = Row(<String, Object?>{
  'code': 'WASH-01',
  'capacity': 12,
});

final code = row.require(AreaFields.code);       // String
final capacity = row.maybe(AreaFields.capacity); // int?

row.set(AreaFields.name, 'Equipment Wash Bay');

This is why field tokens must be reused rather than recreated from matching strings.

DB, UI, and seed hints do not change domain meaning

dart
static const name = TextField(
  'name',
  maxLength: 240,
  nullable: false,
  db: FieldDb(
    indexed: true,
    queryPatterns: [QueryPattern.search, QueryPattern.sort],
  ),
  ui: FieldUI(
    searchable: true,
    sortable: true,
    order: 10,
  ),
  seed: FieldSeed(
    strategy: SeedValueStrategy.label,
    examples: ['Equipment Wash Bay'],
  ),
);

Hints guide interpreters. They do not create a second field definition.

Checkpoint

Create tokens for a controlled equipment record:

  • code and name;
  • serial number;
  • category reference;
  • decimal contact-surface area;
  • multiple document attachments;
  • lifecycle status enum.

Compare your result with the real Ops equipment.dart, then continue to Facets and entities.

Blue is the Vyuh Blueprint documentation surface.