Visual Editor
The vyuh_form_editor package provides a drag-and-drop form designer built on MobX and the Property System. It produces JSON compatible with vyuh_feature_forms.
Architecture
The editor consists of three main components arranged horizontally:
FormEditorStore
The MobX store manages all editor state:
// Simple setup with built-in defaults
final store = FormEditorStore.withDefaults();
// Custom setup with additional field types
final store = FormEditorStore.withDefaults(
additionalFieldTypes: [MyCustomFieldType()],
additionalConditionTypes: [MyCustomCondition()],
additionalActionTypes: [MyCustomAction()],
);
// Descriptor-based setup (recommended for production)
final store = FormEditorStore.fromDescriptors([
FormEditorDescriptor.withDefaults(
fieldTypeDescriptors: [
ReferenceFieldDescriptor(
providers: [myEntityProviderType()],
),
],
),
]);
// Custom registry for full control
final store = FormEditorStore.withRegistry(
registry: FormEditorRegistry(
fieldTypes: [...],
structuralTypes: [...],
conditionTypes: [...],
actionTypes: [...],
),
);Store API
| Method | Description |
|---|---|
formInstance | Current form being edited |
items | All items in display order |
allFields | All fields including nested |
selectedItem | Currently selected item |
selectItem(item) | Select an item |
selectForm() | Select the form itself (show form properties) |
addBlock(block, container:, at:) | Add a compatible field, row, section, or repeating section |
removeBlock(block) | Remove a block from its parent container |
moveBlockWithinContainer(block, to) | Reorder a block inside its current container |
moveBlockToContainer(block, targetContainer:, at:) | Move a block between containers |
undo() | Undo last action |
redo() | Redo last undone action |
toJson() | Export form as JSON |
validate() | Validate the form definition |
isValid | Whether the definition has no errors |
FormEditor Widget
The main widget orchestrates all components:
FormEditor(
store: store,
onFieldsChanged: (fields) {
// React to field list changes
print('Fields: ${fields.length}');
},
headerActions: [
// Custom header actions (optional)
FormEditorAction(
label: 'Save',
icon: Icons.save,
onPressed: () => saveForm(store.toJson()),
),
],
)FormEditorRegistry
The registry manages all registered types and serves as the sole compositor:
final registry = store.registry;
// Access registered types
registry.getFieldTypes(); // All field types
registry.getValidationTypes(); // All validation types
registry.getConditionalRuleConditionTypes(); // All condition types
registry.getConditionalRuleActionTypes(); // All action types
// Composition: convert vyuh models to editor instances
registry.fieldFromVyuh(vyuhField);
registry.sectionFromVyuh(vyuhSection);
registry.ruleFromVyuh(vyuhRule);
// Validate registry consistency
final warnings = registry.validate();Field Palette
The left panel shows all available field types organized by category. Users drag fields from the palette onto the canvas:
- Structure -- Row, Section, Repeating Section
- Basic -- TextField, SelectField, BooleanField, DateTimeField
- Numeric -- NumberField, SliderField, RangeSliderField
- Advanced -- PhoneNumberField, FormulaField, ReferenceField
- Media -- ImagePickerField, FilePickerField
Form Canvas
The center panel displays the form layout with:
- Drag-and-drop reordering
- Row layout controls with per-field spans
- Field cards with type icons and validation indicators
- Section containers with collapsible headers
- Repeating section templates
- Multi-selection support (Ctrl/Cmd-click, Shift-click)
Properties Panel
The right panel shows configuration for the selected item:
- Field tab -- Name, title, placeholder, help, and field-specific properties
- Validation tab -- Add/remove/configure validations
- Layout tab -- Layout type selection and configuration (when multiple layouts available)
- Provider tab -- Reference-field provider configuration
- Rules tab -- Add/remove/configure rules (condition + action pairs)
When the form itself is selected, the properties panel shows form-level settings (title, description, spacing).
Keyboard Shortcuts
The editor supports keyboard shortcuts for common operations:
- Delete / Backspace -- Remove selected field
- Ctrl+Z / Cmd+Z -- Undo
- Ctrl+Shift+Z / Cmd+Shift+Z -- Redo
- Ctrl+D / Cmd+D -- Duplicate selected field
- Ctrl+A / Cmd+A -- Select all
- Escape -- Clear selection
Import/Export
Export to JSON
final json = store.toJson();
// json is a Map<String, dynamic> compatible with vyuh_feature_formsImport from JSON
// Parse existing form JSON with vyuh_feature_forms, then compose editor blocks
final formInstance = registry.formType.fromVyuh(existingForm);
for (final block in existingForm.items) {
final item = switch (block) {
FormField f => registry.fieldFromVyuh(f),
FormRowBlock r => registry.rowFromVyuh(r),
FormSection s => registry.sectionFromVyuh(s),
RepeatingSection r => registry.repeatingSectionFromVyuh(r),
_ => null,
};
if (item != null) formInstance.addBlock(item);
}
store.importFormCommand(formInstance);Validation
The store provides form definition validation:
final errors = store.validate();
for (final error in errors) {
print('${error.type}: ${error.propertyLabel} - ${error.errorDetail}');
if (error.fieldTitle != null) {
print(' Field: ${error.fieldTitle}');
}
}Validation checks:
- Form must have at least one field
- Field names must be unique
- Required properties must have values
- Property validators (e.g., identifier format for field names)
FormEditorDescriptor
The declarative way to configure the editor:
final descriptor = FormEditorDescriptor.withDefaults(
fieldTypeDescriptors: [
// Customize specific field types
ReferenceFieldDescriptor(
providers: [
myEntityProviderType(['course', 'instructor']),
],
),
],
);
final store = FormEditorStore.fromDescriptors([descriptor]);ReferenceFieldDescriptor.providers accepts editor ProviderType instances. The built-in reference field registers the static provider by default; app or entity packages can contribute additional provider factories without subclassing ProviderType.
Multiple descriptors can be merged -- each feature contributes its own, and the store builds the unified registry.
Next Steps
- Architecture -- System design overview
- Building Forms -- Programmatic form construction
- API: Editor -- Complete editor API reference