Skip to content

Dependencies

Dependencies is the data context a fragment is inflated with. It holds the values, objects, and functions that EL expressions read — when a fragment renders ${user.email}, it's reading user.email from its Dependencies instance. Controllers write into it; fragments read from it. See State and Reactivity for how those reads become live-updating UI.

Reading and Writing

setValue and getValue address nested data with dot and bracket paths. Writing creates any intermediate collections that don't exist yet; reading a path that doesn't exist returns null instead of throwing.

final dependencies = Dependencies();
dependencies.setValue("users[0].name", "Maya Chen");
dependencies.setValue("users[0].email", "[email protected]");

print(dependencies.getValue("users[0].name"));   // Maya Chen
print(dependencies.getValue("users[3].email"));  // null — no error

The constructor accepts the same path-keyed map:

final dependencies = Dependencies({
  "users[0].name": "Maya Chen",
  "users[0].email": "[email protected]",
});

Fragments read the same structure through EL:

<forEach var="user" items="${users}">
    <Row>
        <Text data="${user.name}"/>
        <Text data="${user.email}"/>
    </Row>
</forEach>

Note

The index operators dependencies["key"] and dependencies["key"] = value are plain map access — they treat the key as a single literal string. Use setValue/getValue when you want dot/bracket paths.

Global Data

Prefixing a path with global. stores the value in a single store shared by every Dependencies instance in the app, for its entire lifetime. Use it for app-wide state — the signed-in user, session settings — and keep everything else local, so one screen's data can't leak into another.

dependencies.setValue("global.session.user.name", "Maya Chen");

Any fragment can read it, whatever Dependencies instance it was inflated with:

<Text data="${global.session.user.name}"/>

Functions

Dependencies hold functions the same way they hold data. Controllers bind callbacks in bindDependencies(), and fragments invoke them — bound directly to a widget attribute, or called with arguments through a <callback> tag.

@override
void bindDependencies() {
  dependencies.setValue("onSave", onSave);
}
<Button onPressed="${onSave}">
    <Text data="Save"/>
</Button>

See Controllers for the full pattern.

Reacting to Changes

listenForChanges returns a ValueNotifier for a path. Writes that go through that path update the notifier, which is what the <ValueListener> component uses to rebuild its children:

<ValueListener varName="user.email">
    <Text data="${user.email}"/>
</ValueListener>
dependencies.setValue("user.email", "[email protected]");  // ValueListener rebuilds

The rules for which writes notify which listeners have real subtlety — writes through a path notify, writes below one may not. Read State and Reactivity before relying on them.

Scoping

Every fragment inflation receives a Dependencies instance, but not necessarily a new one. The <fragment> tag's dependenciesScope attribute controls whether a child fragment shares its parent's instance (inherit), gets a snapshot (copy), or starts empty (new) — and which one is the automatic default. That behavior is owned by Fragments; the summary that matters here: global. data ignores scoping entirely, and everything else follows it.

API Summary

Member Behavior
Dependencies([data]) Creates an instance, applying addAll(data) when provided
setValue(path, value) Writes a value; creates missing intermediate collections
getValue(path) Reads a value; returns null for missing paths
removeValue(path) Removes and returns a value
addAll(map) Applies a map of path/value pairs via setValue
putIfAbsent(key, value) Writes only when the key has no value yet
listenForChanges(path, [initialValue, defaultValue]) Returns the path's ValueNotifier, creating it if needed
copy({addData, preserveData}) Shallow copy — the mechanism behind copy scoping
[key] / [key] = value Plain map access; no path resolution

All path-accepting members understand the global. prefix.