Skip to content

Conditions

Conditions control the visibility and enabled state of properties reactively. When a dependent property value changes, conditions are re-evaluated automatically by the PropertyCollection.

How Conditions Work

Each Property<T> can have two conditions:

  • visibleCondition -- controls whether the property is shown in the editor
  • enabledCondition -- controls whether the property is editable
dart
b.integer('capacity', 'Max Capacity',
  defaultValue: 30,
  visibleCondition: PropertyCollectionBuilder.whenTrue('enrollmentOpen'),
  enabledCondition: PropertyCollectionBuilder.whenNot<String>('status', 'archived'),
);

Evaluation Pipeline

  1. Each condition declares its dependencies -- the property keys it depends on
  2. PropertyCollection builds a reverse dependency map: dependency → {dependent properties}
  3. When FormGroup.valueChanges fires, the collection finds all affected properties
  4. Each affected property's conditions are re-evaluated via condition.evaluate(collection)
  5. Visibility updates via ValueNotifier<bool> (triggers ValueListenableBuilder in the editor)
  6. Enabled state updates via PropertyControl.markAsEnabled() / markAsDisabled()

Condition Types

There are 12 built-in condition types.

EqualsCondition<T>

True when a property equals a specific value.

dart
EqualsCondition<String>('status', 'active')

// Builder shorthand
PropertyCollectionBuilder.when<String>('status', 'active')

NotEqualsCondition<T>

True when a property does not equal a specific value.

dart
NotEqualsCondition<String>('status', 'archived')

// Builder shorthand
PropertyCollectionBuilder.whenNot<String>('status', 'archived')

InCondition<T>

True when a property value is in a set of allowed values.

dart
InCondition<String>('level', {'intermediate', 'advanced'})

// Builder shorthand
PropertyCollectionBuilder.whenIn<String>('level', {'intermediate', 'advanced'})

NotInCondition<T>

True when a property value is not in a set of values.

dart
NotInCondition<String>('status', {'deleted', 'archived'})

// Builder shorthand
PropertyCollectionBuilder.whenNotIn<String>('status', {'deleted', 'archived'})

HasValueCondition

True when a property exists and has a non-null value.

dart
HasValueCondition('instructor')

// Builder shorthand
PropertyCollectionBuilder.whenHasValue('instructor')

ComparisonCondition<T extends num>

True when a numeric property passes a comparison. Supports four operators.

dart
ComparisonCondition<int>(
  propertyKey: 'capacity',
  compareValue: 10,
  operator: ComparisonOperator.greaterThan,
)

// Builder shorthands
PropertyCollectionBuilder.whenGreaterThan<int>('capacity', 10)
PropertyCollectionBuilder.whenLessThan<int>('capacity', 100)

Available operators:

OperatorDescription
ComparisonOperator.greaterThanvalue > compareValue
ComparisonOperator.greaterThanOrEqualvalue >= compareValue
ComparisonOperator.lessThanvalue < compareValue
ComparisonOperator.lessThanOrEqualvalue <= compareValue

AndCondition

True when all child conditions are true.

dart
AndCondition([
  EqualsCondition<bool>('advanced', true),
  ComparisonCondition<int>(
    propertyKey: 'capacity',
    compareValue: 0,
    operator: ComparisonOperator.greaterThan,
  ),
])

// Builder shorthand
PropertyCollectionBuilder.whenAll([
  PropertyCollectionBuilder.whenTrue('advanced'),
  PropertyCollectionBuilder.whenGreaterThan<int>('capacity', 0),
])

Dependencies are the union of all child dependencies.

OrCondition

True when any child condition is true.

dart
OrCondition([
  EqualsCondition<String>('mode', 'auto'),
  EqualsCondition<bool>('override', true),
])

// Builder shorthand
PropertyCollectionBuilder.whenAny([
  PropertyCollectionBuilder.when<String>('mode', 'auto'),
  PropertyCollectionBuilder.whenTrue('override'),
])

NotCondition

Inverts another condition.

dart
NotCondition(EqualsCondition<bool>('locked', true))

There is no builder shorthand for NotCondition. Use the constructor directly.

AlwaysTrueCondition

Always returns true. Useful as a default or placeholder.

dart
const AlwaysTrueCondition()

AlwaysFalseCondition

Always returns false. Useful for permanently hiding a property.

dart
const AlwaysFalseCondition()

CustomCondition

Uses a function for evaluation. Provides maximum flexibility when built-in conditions are insufficient.

dart
CustomCondition(
  dependencies: ['level', 'capacity'],
  evaluator: (collection) {
    final level = collection.getValue<String>('level');
    final capacity = collection.getValue<int>('capacity');
    return level == 'advanced' && capacity > 20;
  },
)

// Builder shorthand
PropertyCollectionBuilder.whenCustom(
  dependencies: ['level', 'capacity'],
  evaluator: (collection) {
    final level = collection.getValue<String>('level');
    final capacity = collection.getValue<int>('capacity');
    return level == 'advanced' && capacity > 20;
  },
)

LMS Example: Conditional Course Settings

dart
enum EnrollmentType { open, limited, invitation }

final b = PropertyCollectionBuilder();

b.group('enrollment', 'Enrollment');

b.enumeration<EnrollmentType>('enrollmentType', 'Enrollment Type',
  options: [
    EnumOption(EnrollmentType.open, 'Open'),
    EnumOption(EnrollmentType.limited, 'Limited'),
    EnumOption(EnrollmentType.invitation, 'Invitation Only'),
  ],
  defaultValue: EnrollmentType.open,
);

// Only show capacity when enrollment is limited
b.integer('capacity', 'Max Capacity',
  defaultValue: 30,
  visibleCondition: PropertyCollectionBuilder.when<EnrollmentType>(
    'enrollmentType', EnrollmentType.limited,
  ),
);

// Show waitlist option when limited AND capacity > 0
b.boolean('enableWaitlist', 'Enable Waitlist',
  defaultValue: false,
  visibleCondition: PropertyCollectionBuilder.whenAll([
    PropertyCollectionBuilder.when<EnrollmentType>(
      'enrollmentType', EnrollmentType.limited,
    ),
    PropertyCollectionBuilder.whenGreaterThan<int>('capacity', 0),
  ]),
);

// Show invitation code when invitation type
b.string('invitationCode', 'Invitation Code',
  visibleCondition: PropertyCollectionBuilder.when<EnrollmentType>(
    'enrollmentType', EnrollmentType.invitation,
  ),
);

Condition Summary Table

ConditionDependenciesBuilder Shorthand
EqualsCondition<T>[propertyKey]when<T>(key, value)
NotEqualsCondition<T>[propertyKey]whenNot<T>(key, value)
InCondition<T>[propertyKey]whenIn<T>(key, values)
NotInCondition<T>[propertyKey]whenNotIn<T>(key, values)
HasValueCondition[propertyKey]whenHasValue(key)
ComparisonCondition<T>[propertyKey]whenGreaterThan<T>() / whenLessThan<T>()
AndConditionUnion of childrenwhenAll(conditions)
OrConditionUnion of childrenwhenAny(conditions)
NotConditionSame as child(direct constructor)
AlwaysTrueCondition[](direct constructor)
AlwaysFalseCondition[](direct constructor)
CustomConditionExplicit listwhenCustom(deps, fn)

Next Steps