Skip to content

Dynamic Behavior

This guide covers using the rules system to add dynamic behavior to forms: showing/hiding fields, enabling/disabling fields, setting values, and adding conditional validations.

Show/Hide Fields

The most common rule pattern. Use showWhen to show a field when a condition is met (hidden otherwise), or hideWhen for the inverse.

Basic Visibility

dart
// Show payment fields only for paid enrollment
.select('payment_method', title: 'Payment Method')
  .option('credit_card', title: 'Credit Card')
  .option('bank_transfer', title: 'Bank Transfer')
  .showWhen(When.fieldEquals('enrollment_type', 'paid'))
  .required()

Hide When Empty

dart
// Hide confirmation field until email is entered
.text('email_confirm', title: 'Confirm Email')
  .hideWhen(When.fieldEmpty('email'))
  .required()

Boolean Toggle

dart
// Show international fields when checkbox is checked
.text('passport_number', title: 'Passport Number')
  .showWhen(When.fieldTrue('is_international'))
  .required()
.text('visa_type', title: 'Visa Type')
  .showWhen(When.fieldTrue('is_international'))

Enable/Disable Fields

Use enableWhen and disableWhen to control field interactivity.

dart
// Disable course selection after enrollment is confirmed
.select('course', title: 'Course')
  .option('CS101', title: 'Intro to CS')
  .option('CS201', title: 'Data Structures')
  .disableWhen(When.fieldTrue('is_confirmed'))

// Enable "Other" text field only when "Other" is selected
.text('other_reason', title: 'Please specify')
  .enableWhen(When.fieldEquals('reason', 'other'))

Section-Level Rules

Sections can have visibility rules that show/hide all child fields together:

dart
.section('Payment Details', (s) => s
  .select('payment_method', title: 'Payment Method')
    .option('credit_card', title: 'Credit Card')
    .option('bank_transfer', title: 'Bank Transfer')
    .required()
  .text('card_number', title: 'Card Number')
    .showWhen(When.fieldEquals('payment_method', 'credit_card'))
    .required()
  .text('bank_account', title: 'Bank Account')
    .showWhen(When.fieldEquals('payment_method', 'bank_transfer'))
    .required(),
  rules: [When.fieldEquals('enrollment_type', 'paid').show()],
)

The entire "Payment Details" section appears only when enrollment type is "paid". Within it, card number and bank account fields toggle based on the payment method.

Compound Conditions

Combine multiple conditions with AND/OR logic:

All Conditions Must Match (AND)

dart
// Show international payment fields only for paid + international
.text('swift_code', title: 'SWIFT Code')
  .showWhen(When.allOf([
    When.fieldEquals('enrollment_type', 'paid'),
    When.fieldTrue('is_international'),
  ]))

Any Condition Must Match (OR)

dart
// Show special instructions for either paid or sponsored students
.text('special_instructions', title: 'Special Instructions')
  .showWhen(When.anyOf([
    When.fieldEquals('enrollment_type', 'paid'),
    When.fieldEquals('enrollment_type', 'sponsored'),
  ]))

Dynamic Validation

Add or remove validations based on form state using ValidationAction:

dart
// Make payment method required only for paid enrollment
TextField(
  name: 'payment_method',
  title: 'Payment Method',
  rules: [
    FormFieldRule(
      condition: StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      action: ValidationAction(
        validationType: 'required',
        errorMessage: 'Payment method is required for paid enrollment',
        addWhenTrue: true,
      ),
    ),
  ],
)

When enrollment_type changes to "paid", the required validation is activated. When it changes to something else, the validation is deactivated.

Setting Values

Use SetValueAction to set a field's value when a condition is met:

dart
// When "same as billing" is checked, auto-fill shipping address
TextField(
  name: 'shipping_address',
  title: 'Shipping Address',
  rules: [
    FormFieldRule(
      condition: BooleanCondition(
        targetField: 'same_as_billing',
        operator: BooleanOperator.isTrue,
      ),
      action: SetValueAction(value: ''),
    ),
  ],
)

Focus Management

Move focus to a specific field when a condition changes:

dart
TextField(
  name: 'payment_method',
  title: 'Payment Method',
  rules: [
    FormFieldRule(
      condition: StringCondition(
        targetField: 'enrollment_type',
        operator: StringOperator.equals,
        value: 'paid',
      ),
      action: FocusAction(
        targetFieldName: 'payment_method',
        focusWhenTrue: true,
      ),
    ),
  ],
)

Compound Actions

Apply multiple actions simultaneously:

dart
FormFieldRule(
  condition: StringCondition(
    targetField: 'enrollment_type',
    operator: StringOperator.equals,
    value: 'paid',
  ),
  action: CompoundAction(actions: [
    VisibilityAction(showWhenTrue: true),
    ValidationAction(
      validationType: 'required',
      errorMessage: 'Required for paid enrollment',
      addWhenTrue: true,
    ),
  ]),
)

Runtime vs. Visual Editor Defaults

The runtime model supports VisibilityAction, EnabledAction, FocusAction, SetValueAction, CompoundAction, and ValidationAction. The default visual editor palette exposes the common field-editing actions: visibility, enabled state, and validation. Apps can register additional action types through FormEditorDescriptor when those actions should be authored visually.

LMS Example: Enrollment with Payment Logic

dart
final form = FormBuilder(title: 'Course Enrollment')
  // Student info
  .text('name', title: 'Full Name').required()
  .text('email', title: 'Email').required().email()

  // Course and enrollment type
  .select('course', title: 'Course')
    .option('flutter', title: 'Flutter Development')
    .option('dart', title: 'Dart Fundamentals')
    .required()
  .select('enrollment_type', title: 'Enrollment Type')
    .option('open', title: 'Open (Free)')
    .option('paid', title: 'Paid')
    .option('sponsored', title: 'Sponsored')
    .required()

  // International student flag
  .boolean('is_international', title: 'International Student')

  // Payment section (visible for paid enrollment only)
  .section('Payment Information', (s) => s
    .select('payment_method', title: 'Payment Method')
      .option('credit_card', title: 'Credit Card')
      .option('bank_transfer', title: 'Bank Transfer')
      .required()
    .text('card_number', title: 'Card Number')
      .showWhen(When.fieldEquals('payment_method', 'credit_card'))
      .required()
    .text('swift_code', title: 'SWIFT/BIC Code')
      .showWhen(When.allOf([
        When.fieldEquals('payment_method', 'bank_transfer'),
        When.fieldTrue('is_international'),
      ]))
      .required(),
    rules: [When.fieldEquals('enrollment_type', 'paid').show()],
  )

  // Sponsor section (visible for sponsored enrollment only)
  .section('Sponsor Details', (s) => s
    .text('sponsor_name', title: 'Sponsor Name').required()
    .text('sponsor_email', title: 'Sponsor Email').required().email(),
    rules: [When.fieldEquals('enrollment_type', 'sponsored').show()],
  )

  .build();

Next Steps