Skip to content

Rules System

The rules system provides declarative dynamic behavior for form fields. Each rule is a pair: a condition that evaluates form state, and an action that modifies a field when the condition is met.

FormFieldRule

dart
class FormFieldRule implements SchemaItem {
  final RuleCondition condition;
  final RuleAction action;
}

Rules are evaluated reactively. When any field that a condition depends on changes, the rule re-evaluates and the action is applied.

Conditions

Conditions evaluate form field values and return a boolean. All conditions implement RuleCondition:

dart
abstract class RuleCondition implements SchemaItem {
  Set<String> getDependencies();
  bool evaluate(Map<String, dynamic> formValues);
}

6 Condition Types

BooleanCondition

Checks if a boolean field is true or false.

dart
BooleanCondition(
  targetField: 'is_international',
  operator: BooleanOperator.isTrue,  // or isFalse
)

StringCondition

String comparison with 9 operators.

dart
StringCondition(
  targetField: 'enrollment_type',
  operator: StringOperator.equals,
  value: 'paid',
  caseSensitive: false,  // default
)

StringOperator values: equals, notEquals, contains, notContains, startsWith, endsWith, matches (regex), isEmpty, isNotEmpty

NumericCondition

Numeric comparison with 6 operators.

dart
NumericCondition(
  targetField: 'team_size',
  operator: NumericOperator.greaterThan,
  value: 5,
  maxValue: null,  // used with 'between'
)

NumericOperator values: equal, greaterThan, lessThan, greaterEqual, lessEqual, between

DateCondition

Date comparison with 4 operators.

dart
DateCondition(
  targetField: 'start_date',
  operator: DateOperator.after,
  date: DateTime.now(),
  endDate: null,  // used with 'between'
)

DateOperator values: before, after, on, between

EmptyCondition

Checks if a field value is null, empty string, empty list, or empty map.

dart
EmptyCondition(targetField: 'notes')

CompoundCondition

Combines multiple conditions with AND/OR logic.

dart
CompoundCondition(
  operator: ConditionOperator.and,  // or ConditionOperator.or
  conditions: [
    StringCondition(targetField: 'type', operator: StringOperator.equals, value: 'paid'),
    BooleanCondition(targetField: 'is_international', operator: BooleanOperator.isTrue),
  ],
)

Actions

Actions modify field behavior when their condition evaluates. All actions implement RuleAction:

dart
abstract class RuleAction implements SchemaItem {
  void apply(FormField targetField, bool conditionMet, BuildContext context);
}

6 Action Types

VisibilityAction

Shows or hides a field based on the condition.

dart
VisibilityAction(showWhenTrue: true)  // Show when condition met, hide otherwise
VisibilityAction(showWhenTrue: false) // Hide when condition met, show otherwise

EnabledAction

Enables or disables a field based on the condition.

dart
EnabledAction(enableWhenTrue: true)   // Enable when condition met
EnabledAction(enableWhenTrue: false)  // Disable when condition met

SetValueAction

Sets a field's value when the condition is met.

dart
SetValueAction(value: 'default_course')

FocusAction

Moves focus to a specific field when the condition is met.

dart
FocusAction(
  targetFieldName: 'payment_method',
  focusWhenTrue: true,
)

ValidationAction

Adds or removes a validation rule dynamically based on the condition.

dart
ValidationAction(
  validationType: 'required',  // 'required', 'email', 'pattern'
  errorMessage: 'Payment method is required for paid enrollments',
  addWhenTrue: true,  // Add validation when condition met
)

Uses the field's DynamicValidator to activate/deactivate validations at runtime.

CompoundAction

Applies multiple actions together.

dart
CompoundAction(actions: [
  VisibilityAction(showWhenTrue: true),
  EnabledAction(enableWhenTrue: true),
])

LMS Examples

Show Payment Fields for Paid Enrollment

dart
// Payment method: visible only when enrollment type is 'paid'
TextField(
  name: 'payment_method',
  title: 'Payment Method',
  rules: [
    FormFieldRule(
      condition: StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      action: VisibilityAction(showWhenTrue: true),
    ),
  ],
)

Make Field Required When Visible

dart
// Payment method: required only when enrollment type is 'paid'
TextField(
  name: 'payment_method',
  title: 'Payment Method',
  rules: [
    // Show when paid
    FormFieldRule(
      condition: StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      action: VisibilityAction(showWhenTrue: true),
    ),
    // Also make required when paid
    FormFieldRule(
      condition: StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      action: ValidationAction(
        validationType: 'required',
        errorMessage: 'Payment method is required',
        addWhenTrue: true,
      ),
    ),
  ],
)

Compound Condition: International Paid Students

dart
FormFieldRule(
  condition: CompoundCondition(
    operator: ConditionOperator.and,
    conditions: [
      StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      BooleanCondition(
        targetField: 'is_international',
        operator: BooleanOperator.isTrue,
      ),
    ],
  ),
  action: VisibilityAction(showWhenTrue: true),
)

When DSL

The When class provides a fluent DSL for creating rules:

dart
// String conditions
When.fieldEquals('type', 'paid')
When.fieldNotEquals('type', 'free')
When.fieldContains('name', 'admin')
When.fieldEmpty('notes')
When.fieldNotEmpty('email')

// Boolean conditions
When.fieldTrue('is_active')
When.fieldFalse('is_draft')

// Compound conditions
When.allOf([When.fieldEquals('type', 'paid'), When.fieldTrue('international')])
When.anyOf([When.fieldEquals('type', 'paid'), When.fieldEquals('type', 'sponsored')])

// Actions
When.fieldEquals('type', 'paid').show()    // VisibilityAction(showWhenTrue: true)
When.fieldEquals('type', 'paid').hide()    // VisibilityAction(showWhenTrue: false)
When.fieldEquals('type', 'paid').enable()  // EnabledAction(enableWhenTrue: true)
When.fieldEquals('type', 'paid').disable() // EnabledAction(enableWhenTrue: false)

Used in the FormBuilder DSL:

dart
.text('payment_method', title: 'Payment Method')
  .showWhen(When.fieldEquals('enrollment_type', 'paid'))
  .required()

Section Rules

FormSection also supports rules for section-level visibility:

dart
FormBuilder(title: 'Application')
  .section('International Details', (s) => s
    .text('passport', title: 'Passport Number')
    .text('visa_type', title: 'Visa Type'),
    rules: [When.fieldTrue('is_international').show()],
  )
  .build();

Next Steps