Core Classes
XWidget EL includes two foundational classes that power expression evaluation and data
management: Dependencies and Model. These classes are part of the xwidget_el package
and can be used independently of XWidget.
Dependencies
The Dependencies class is a key-value store that serves as the data context
for expression evaluation. When an EL expression references a variable like ${user.name},
the value is resolved from a Dependencies instance.
Basic Usage
final deps = Dependencies();
deps.setValue("user.name", "Mike");
deps.setValue("user.email", "[email protected]");
final parser = ELParser();
final name = parser.evaluate("user.name", deps); // -> "Mike"
Dot/Bracket Notation
Values can be read and written using dot and bracket notation for nested access.
Reads resolve to null if the path doesn't exist. Writes create the necessary
structures automatically.
final deps = Dependencies();
deps.setValue("users[0].name", "Alice");
deps.setValue("users[1].name", "Bob");
deps.getValue("users[0].name"); // -> "Alice"
deps.getValue("users[2].name"); // -> null
Global Data
Prefix a key with global. to store data that is shared across all Dependencies
instances.
final deps1 = Dependencies();
deps1.setValue("global.theme", "dark");
final deps2 = Dependencies();
deps2.getValue("global.theme"); // -> "dark"
Change Notifications
Wrap a value in a ValueNotifier to receive change notifications. This is the
mechanism behind XWidget's reactive <ValueListener> component.
final deps = Dependencies();
deps.setValue("count", 0);
final notifier = deps.listenForChanges("count");
notifier.addListener(() {
print("count changed to ${notifier.value}");
});
deps.setValue("count", 1); // triggers listener
Custom Functions
Any Dart function stored in Dependencies becomes callable from EL expressions.
Functions can only accept positional arguments.
For full documentation, see Dependencies.
Model
The Model class is a structured data container built on top of Map<String, dynamic>.
It provides property access, null safety, data transformation, type conversion, and
instance management.
Basic Usage
class Profile extends Model {
String get username => getValue("username!");
String get email => getValue("email!");
String? get name => getValue("name");
Profile(super.data, {super.translation, super.immutable});
}
final profile = Profile({
"username": "mike.smith",
"email": "[email protected]",
"name": "Mike Smith",
});
The ! suffix on getValue("username!") asserts the value is non-null — an exception
is thrown if it's missing.
Property Transformers
Register models with PropertyTransformers to define the target structure and
enable automatic type conversion when loading data.
Models.register<Profile>(Profile.new, const [
PropertyTransformer<String>("username"),
PropertyTransformer<String>("email"),
PropertyTransformer<String?>("name"),
PropertyTransformer<DateTime?>("lastLogin"),
]);
Property Translation
Map source data structures to your model when the source keys don't match your model's properties.
final profile = Profile({
"user_name": "mike.smith",
"user_email": "[email protected]",
}, translation: PropertyTranslation({
"user_name": "username",
"user_email": "email",
}));
Type Converters
Register custom type converters for types not covered by the built-in converters
(String, int, double, bool, DateTime, Duration, Color).
TypeConverters.register<Money>((value) {
if (value is Money) return value;
if (value is String) return Money.parse(value, isoCode: 'USD');
if (value is int) return Money.fromInt(value, isoCode: 'USD');
throw Exception("Unable to convert to Money: $value");
});
Instance Management
Use singleInstance or keyedInstance to prevent duplicate model objects
representing the same data.
// Singleton — one instance per type
final profile = Model.singleInstance<Profile>(
factory: Profile.new,
data: data,
);
// Keyed — one instance per unique key
final topic = Model.keyedInstance<Topic>(
factory: Topic.new,
data: data,
);
For full documentation, see Models.
ELParser
The ELParser class parses and evaluates expression strings. It can be used
standalone without XWidget.
Basic Usage
final parser = ELParser();
final dependencies = Dependencies({
"users": [
{"name": "Mike Jones"},
{"name": "Sally Smith"},
],
"indexes": [1, 0, 2],
});
Parse and Evaluate Separately
final result = parser.parse("users[indexes[0]].name + ', ' + users[0].name");
result.value.evaluate(dependencies); // -> "Sally Smith, Mike Jones"
Evaluate in One Step
Evaluate Embedded Expressions
For expressions embedded in a larger string:
For full expression syntax, see Rules.