Design Philosophy
The Vyuh Property System is built on five core principles that guide every design decision.
1. Type Safety First
Every property carries its type through the entire pipeline. Property<T> wraps a typed value, PropertyControl<T> provides reactive access, PropertyEditor<T> renders the UI, and JsonConverter<T> handles serialization. There are no Map<String, dynamic> leaking between layers.
// The type T flows through the entire chain
final duration = IntProperty(
key: 'duration',
label: 'Duration',
defaultValue: 60,
validators: [PropertyValidators.min(1)], // PropertyValidator<num>
);
// Type-safe access
int value = duration.value; // int, not dynamic
duration.value = 90; // compile-time checked
String json = duration.toJson(); // uses IntJsonConverterThis eliminates an entire class of runtime errors. If a property is declared as IntProperty, it cannot accidentally hold a string, and its editor will always render a number input.
2. Reactive by Default
Property values are reactive. When a value changes, the UI updates. When a dependent value changes, conditions re-evaluate automatically. This reactivity is powered by reactive_forms internally, but exposed through the simpler PropertyControl<T> and ValueNotifier<bool> abstractions.
// Visibility reacts to value changes automatically
b.boolean('showAdvanced', 'Show Advanced', defaultValue: false);
b.integer('threadCount', 'Thread Count',
visibleCondition: PropertyCollectionBuilder.whenTrue('showAdvanced'),
);You never need to write manual listeners for condition evaluation. The PropertyCollection watches for value changes on the underlying FormGroup and re-evaluates all dependent conditions automatically.
3. Registry-Based Extension
The PropertySystem class acts as a central registry for editors, converters, and custom type support. Default types are registered automatically on first use. New types can be added at any time:
// Register a custom type with editor and converter
PropertySystem.register<Color>(
editor: ColorPropertyEditor(),
converter: ColorJsonConverter(),
);
// Now ColorProperty works with the same pipeline
final bg = Property<Color>(/* ... */);
bg.createEditor(context: context); // uses ColorPropertyEditor
bg.toJson(); // uses ColorJsonConverterThe registry supports both type-based lookup (PropertySystem.getEditor<int>()) and custom key lookup (PropertySystem.getEditor<String>('readonly:string')) for specialized editors.
4. Declarative Conditions
Visibility and enabled state are expressed as declarative condition objects, not imperative code. This makes conditions serializable, composable, and testable:
// Declarative: what, not how
visibleCondition: AndCondition([
EqualsCondition<bool>('advanced', true),
ComparisonCondition<int>(
propertyKey: 'capacity',
compareValue: 10,
operator: ComparisonOperator.greaterThan,
),
])
// Composable with convenience methods
visibleCondition: PropertyCollectionBuilder.whenAll([
PropertyCollectionBuilder.whenTrue('advanced'),
PropertyCollectionBuilder.whenGreaterThan<int>('capacity', 10),
])Conditions declare their dependencies explicitly, so the system only evaluates conditions when relevant values change.
5. Framework Independence
The Property System has no dependency on vyuh_core. It is a standalone Flutter package that can be used in any Flutter application. This means:
- No framework lock-in -- Use it in a Vyuh CDX app or a plain Flutter project
- Minimal dependencies -- Only
flutterandreactive_forms - Clear abstraction boundary -- The
reactive_formsdependency is wrapped behindPropertyControl<T>, so consumers never need to import it directly - Testable in isolation -- Property logic can be tested without a full Vyuh platform
The optional reactive_forms_interop.dart import is available for advanced scenarios where direct FormControl or ReactiveTextField access is needed, but it is strictly opt-in.
Design Consequences
These principles lead to several architectural characteristics:
| Characteristic | How It Manifests |
|---|---|
| Single source of truth | Each property owns its control. No separate state management layer needed. |
| Fluent builder | PropertyCollectionBuilder provides a concise API while preserving full type safety. |
| Automatic editor selection | property.createEditor() looks up the editor from the registry. No manual widget mapping. |
| Condition evaluation | Driven by FormGroup.valueChanges, conditions update without explicit triggers. |
| Serialization parity | toJson() / fromJson() use the same converter registry, ensuring round-trip consistency. |
| Deep cloning | PropertyCollection.clone() and copyWith() enable safe snapshotting for undo/redo. |
Next Steps
- Architecture -- See how these principles map to code
- Quick Start -- Build a settings panel hands-on
- Property Types -- Explore all built-in types