Skip to content

Repeating Sections

RepeatingSection creates dynamic arrays of field groups. Users can add and remove instances of the template fields, with configurable min/max constraints.

Basic Usage

dart
RepeatingSection(
  title: 'Course Modules',
  name: 'modules',
  template: [
    TextField(
      name: 'module_title',
      title: 'Module Title',
      validations: [RequiredValidation(errorMessage: 'Title is required')],
    ),
    NumberField(
      name: 'duration_hours',
      title: 'Duration (hours)',
      allowDecimal: true,
      validations: [
        RequiredValidation(errorMessage: 'Duration is required'),
        NumberRangeValidation(min: 0.5, max: 40, errorMessage: 'Between 0.5 and 40 hours'),
      ],
    ),
  ],
  minInstances: 1,
  maxInstances: 20,
  displayMode: RepeatDisplayMode.expanded,
)

Properties

PropertyTypeDefaultDescription
titleStringrequiredSection heading
nameStringrequiredNamespace for field names
descriptionString?nullOptional description
templateList<ContentItem>[]Template fields for each instance
minInstancesint1Minimum number of instances
maxInstancesint?nullMaximum instances (null = unlimited)
displayModeRepeatDisplayModeexpandedDisplay style
collapsiblebooltrueWhether instances can collapse
hSpacingdouble?nullHorizontal spacing override
vSpacingdouble?nullVertical spacing override

Display Modes

Expanded

All instances are always visible:

dart
RepeatingSection(
  displayMode: RepeatDisplayMode.expanded,
  // ...
)

Accordion

Each instance is collapsible. Only one is expanded at a time by default:

dart
RepeatingSection(
  displayMode: RepeatDisplayMode.accordion,
  collapsible: true,
  // ...
)

Data Structure

Fields are namespaced with the section name and instance index:

modules.0.module_title = "Introduction"
modules.0.duration_hours = 2
modules.1.module_title = "Advanced Topics"
modules.1.duration_hours = 4

The output in form.currentValues is an array:

json
{
  "modules": [
    { "module_title": "Introduction", "duration_hours": 2 },
    { "module_title": "Advanced Topics", "duration_hours": 4 }
  ]
}

LMS Example: Course Curriculum

A course creation form with repeating modules, each containing repeating lessons:

dart
final form = Form(
  title: 'Course Curriculum',
  items: [
    TextField(
      name: 'course_title',
      title: 'Course Title',
      validations: [RequiredValidation(errorMessage: 'Required')],
    ),
    TextField(
      name: 'course_description',
      title: 'Description',
      validations: [],
    ),
    RepeatingSection(
      title: 'Modules',
      name: 'modules',
      minInstances: 1,
      maxInstances: 12,
      displayMode: RepeatDisplayMode.accordion,
      template: [
        TextField(
          name: 'title',
          title: 'Module Title',
          validations: [RequiredValidation(errorMessage: 'Required')],
        ),
        NumberField(
          name: 'order',
          title: 'Order',
          allowDecimal: false,
          validations: [RequiredValidation(errorMessage: 'Required')],
        ),
        TextField(
          name: 'description',
          title: 'Module Description',
          validations: [],
        ),
        NumberField(
          name: 'duration',
          title: 'Duration (hours)',
          allowDecimal: true,
          validations: [
            NumberRangeValidation(
              min: 0.5,
              max: 40,
              errorMessage: 'Must be 0.5-40 hours',
            ),
          ],
        ),
        BooleanField(
          name: 'is_mandatory',
          title: 'Mandatory Module',
          validations: [],
        ),
      ],
    ),
  ],
);

Instance Management

The canAddInstance property checks whether more instances can be added:

dart
final section = form.items.whereType<RepeatingSection>().first;
if (section.canAddInstance) {
  // Show "Add Module" button
}

The namespaced field name helper:

dart
final fieldName = section.namespacedFieldName(2, 'title');
// => 'modules.2.title'

FormArray Integration

Internally, RepeatingSection uses a FormArray in the parent FormGroup. Each instance is a nested FormGroup within the array:

dart
// Access the FormArray
final formArray = form.formGroup.control('modules') as FormArray;

// Number of instances
final count = formArray.controls.length;

// Access a specific instance's value
final firstModule = formArray.controls[0] as FormGroup;
final title = firstModule.control('title').value;

Next Steps