Skip to content

Property Types

The Property System provides built-in property types for all common value categories. Each type is a final class extending Property<T> with appropriate defaults and editor support.

Type Overview

PropertyType TDefaultBuilder Method
StringPropertyString''string()
IntPropertyint0integer()
DoublePropertydouble0.0decimal()
BoolPropertyboolfalseboolean()
DateTimePropertyDateTimeDateTime.now()dateTime()
OptionalStringPropertyString?nulloptionalText()
OptionalIntPropertyint?nulloptionalInteger()
OptionalDoublePropertydouble?nulloptionalDecimal()
OptionalBoolPropertybool?nulloptionalBoolean()
OptionalDateTimePropertyDateTime?nulloptionalDateTime()
EnumProperty<T>TFirst optionenumeration<T>()
ListProperty<T>List<T>[]list<T>()
UnionPropertyStringSelected key(direct constructor)
ReadOnlyProperty<T>TRequiredreadonly<T>()

StringProperty

The most common property type. Renders as a text field.

dart
final title = StringProperty(
  key: 'title',
  label: 'Course Title',
  defaultValue: '',
  required: true,
  validators: [
    PropertyValidators.minLength(3),
    PropertyValidators.maxLength(200),
  ],
  help: 'The display name for this course',
);

Using the builder:

dart
b.string('title', 'Course Title',
  required: true,
  validators: [PropertyValidators.minLength(3)],
);

IntProperty

For integer values. Renders as a number input.

dart
final duration = IntProperty(
  key: 'duration',
  label: 'Duration (hours)',
  defaultValue: 1,
  validators: [
    PropertyValidators.min(1),
    PropertyValidators.max(200),
  ],
);

Using the builder:

dart
b.integer('duration', 'Duration (hours)',
  defaultValue: 1,
  validators: [PropertyValidators.min(1)],
);

DoubleProperty

For floating-point values. Renders as a decimal number input.

dart
final price = DoubleProperty(
  key: 'price',
  label: 'Course Price',
  defaultValue: 0.0,
  validators: [PropertyValidators.min(0)],
);

Using the builder:

dart
b.decimal('price', 'Course Price',
  defaultValue: 0.0,
  validators: [PropertyValidators.min(0)],
);

BoolProperty

For boolean values. Renders as a toggle switch.

dart
final featured = BoolProperty(
  key: 'featured',
  label: 'Feature on Homepage',
  defaultValue: false,
);

Using the builder:

dart
b.boolean('featured', 'Feature on Homepage', defaultValue: false);

DateTimeProperty

For date and time values. Renders as a date/time picker. Defaults to DateTime.now() if no default is provided.

dart
final startDate = DateTimeProperty(
  key: 'startDate',
  label: 'Start Date',
  required: true,
);

Using the builder:

dart
b.dateTime('startDate', 'Start Date', required: true);

The getDisplayValue() method returns a formatted string like 2026-03-26 14:30.

Optional Variants

Each primitive type has a nullable variant for optional values. The key difference: the default value is null, and the editor supports clearing the field.

dart
// Optional string -- allows null
b.optionalText('subtitle', 'Subtitle');

// Optional integer -- allows null
b.optionalInteger('maxRetries', 'Max Retries');

// Optional double -- allows null
b.optionalDecimal('discount', 'Discount %');

// Optional boolean -- three-state: true, false, null
b.optionalBoolean('override', 'Override Default');

// Optional DateTime -- allows null
b.optionalDateTime('expiresAt', 'Expiration Date');

Optional properties provide convenience getters with fallback values:

dart
OptionalStringProperty prop = ...;
String value = prop.stringValue;  // returns '' if null

OptionalIntProperty prop = ...;
int value = prop.intValue;  // returns 0 if null

OptionalDoubleProperty prop = ...;
double value = prop.doubleValue;  // returns 0.0 if null

EnumProperty<T>

For selecting from a list of typed options. The type T can be any Dart type -- a Dart enum, a String, an int, or a custom class.

dart
enum CourseLevel { beginner, intermediate, advanced }

final level = EnumProperty<CourseLevel>(
  key: 'level',
  label: 'Course Level',
  options: [
    EnumOption(CourseLevel.beginner, 'Beginner',
      description: 'No prerequisites'),
    EnumOption(CourseLevel.intermediate, 'Intermediate'),
    EnumOption(CourseLevel.advanced, 'Advanced'),
  ],
  defaultValue: CourseLevel.beginner,
);

Using the builder:

dart
b.enumeration<CourseLevel>('level', 'Course Level',
  options: [
    EnumOption(CourseLevel.beginner, 'Beginner'),
    EnumOption(CourseLevel.intermediate, 'Intermediate'),
    EnumOption(CourseLevel.advanced, 'Advanced'),
  ],
  defaultValue: CourseLevel.beginner,
);

Auto-Registration

EnumProperty automatically registers a JsonConverter<T> for the enum type on construction if one does not already exist. This means you do not need to call PropertySystem.register<CourseLevel>(converter: ...) manually.

EnumOption<T>

Each option has:

FieldTypeDescription
valueTThe typed value
titleStringDisplay label
descriptionString?Optional tooltip or subtitle

ListProperty<T>

For ordered lists of typed values. Supports add, remove, reorder, and per-item editing.

dart
final prerequisites = ListProperty<String>(
  key: 'prerequisites',
  label: 'Prerequisites',
  defaultValue: [],
  required: true,
  minItems: 1,
  maxItems: 10,
);

Using the builder:

dart
b.list<String>('prerequisites', 'Prerequisites',
  required: true,
  minItems: 1,
  maxItems: 10,
);

List Operations

ListProperty<T> provides collection operations beyond basic value get/set:

dart
final list = ListProperty<String>(key: 'tags', label: 'Tags');

list.add('flutter');
list.add('dart');
list.insert(0, 'mobile');
list.removeAt(1);
list.move(0, 2);         // reorder
list.replaceAt(0, 'web');
list.clear();

// Access the list control for streaming
list.listControl.valueChanges.listen((items) => print(items));
list.listControl.itemControls;  // per-item PropertyControl<T>

PropertyListControl<T>

The listControl property exposes a PropertyListControl<T> abstraction that wraps reactive_forms.FormArray<T>:

MethodDescription
itemsCurrent list of values
lengthNumber of items
valueChangesStream of list changes
add(item)Append an item
removeAt(index)Remove by index
insert(index, item)Insert at position
move(old, new)Reorder items
clear()Remove all items
replaceAt(index, item)Replace at position
itemControlsPer-item PropertyControl<T> list

UnionProperty

For selecting one property from multiple alternatives. The control holds the selected key as a String, while each option contains a full Property for its own value.

dart
final scheduling = UnionProperty(
  key: 'scheduling',
  label: 'Scheduling Mode',
  defaultSelectedKey: 'fixed',
  options: [
    UnionOption(
      key: 'fixed',
      title: 'Fixed Schedule',
      property: StringProperty(
        key: 'fixedDate', label: 'Date', required: true,
      ),
    ),
    UnionOption(
      key: 'flexible',
      title: 'Flexible',
      property: IntProperty(
        key: 'flexDays', label: 'Days to Complete', defaultValue: 30,
      ),
    ),
    UnionOption(
      key: 'selfPaced',
      title: 'Self-Paced',
      property: BoolProperty(
        key: 'hasMentoring', label: 'With Mentoring', defaultValue: false,
      ),
    ),
  ],
);

UnionOption

Each union option has:

FieldTypeDescription
keyStringUnique identifier for this option
titleStringDisplay label
descriptionString?Optional description
propertyPropertyThe property to edit when selected
iconWidget?Optional icon widget
metadataMap<String, dynamic>?Arbitrary metadata

Union Operations

dart
// Get current selection
String key = scheduling.selectedKey;
Property? prop = scheduling.selectedProperty;
UnionOption? opt = scheduling.selectedOption;

// Change selection
scheduling.selectOption('flexible');

JSON Format

UnionProperty serializes as:

json
{
  "selectedKey": "fixed",
  "selectedValue": "2026-04-01"
}

ReadOnlyProperty<T>

For display-only values that cannot be edited through the UI. Useful for computed values, system fields, or audit information.

dart
final createdBy = ReadOnlyProperty<String>(
  key: 'createdBy',
  label: 'Created By',
  defaultValue: 'admin',
);

Using the builder:

dart
b.readonly<String>('createdBy', 'Created By', defaultValue: 'admin');

Custom Formatting

ReadOnlyProperty supports a custom formatter function:

dart
ReadOnlyProperty<DateTime>(
  key: 'createdAt',
  label: 'Created',
  defaultValue: DateTime.now(),
  formatter: (dt) => '${dt.day}/${dt.month}/${dt.year}',
);

Default formatting: DateTime values are formatted as local date/time strings, double values use toStringAsFixed(2), and all others use toString().

Common Property Parameters

All property types share these parameters from Property<T>:

ParameterTypeDescription
keyStringUnique identifier within the collection
labelStringDisplay label for the editor
defaultValueTInitial value
helpString?Help text shown below the editor
groupString?Group identifier for organizing into sections
requiredboolWhether the property must have a value
validatorsList<PropertyValidator>Validation rules
visibleConditionPropertyCondition?When to show this property
enabledConditionPropertyCondition?When to enable this property
customKeyString?Custom editor/converter key for registry lookup
metadataMap<String, dynamic>?Arbitrary metadata

Next Steps