Events
XWidget provides a lightweight event system for cross-component communication. Events let any part of your application broadcast notifications that fragments and controllers can listen for and respond to.
Defining Events
Events are defined as Dart enums and registered with XWidget's event system:
enum AppEvent {
userLoggedIn,
userLoggedOut,
dataRefreshed,
themeChanged,
cartUpdated,
}
void main() {
registerXWidgetEvents(AppEvent.values);
runApp(MyApp());
}
Firing Events
Use postEvent() to broadcast an event. The EventNotifier mixin provides this
method, so mix it into any class that needs to fire events:
class CartController extends Controller with EventNotifier {
Future<void> addItem(Product product) async {
await cart.add(product);
postEvent(AppEvent.cartUpdated, {'itemCount': cart.length});
}
}
Events can include an optional payload of any type.
Listening for Events in Fragments
Use the <EventListener> element to respond to events in your XML fragments. When
the specified event fires, the listener's children are rebuilt:
<EventListener event="AppEvent.cartUpdated">
<Text data="Cart updated — ${cartCount} items"/>
</EventListener>
The <EventListener> triggers a rebuild of its children when the event fires. The
children are re-inflated with the current dependencies, so any values that were
updated in response to the event (e.g., by a controller) will be reflected.
Attributes
| Attribute | Required | Description |
|---|---|---|
event |
Yes | The event to listen for (e.g., AppEvent.cartUpdated) |
onEvent |
No | A Dart callback function (event, payload) to handle the event |
The onEvent callback is useful when you need to process the event payload in Dart
before the children rebuild. Expose it from a controller's dependencies:
class DashboardController extends Controller {
int cartCount = 0;
@override
void bindDependencies() {
dependencies.setValue("cartCount", cartCount);
dependencies.setValue("handleCartUpdate", handleCartUpdate);
}
void handleCartUpdate(Enum event, dynamic payload) {
if (payload is Map) {
cartCount = payload['itemCount'] ?? 0;
dependencies.setValue("cartCount", cartCount);
}
}
}
<Controller name="DashboardController">
<EventListener event="AppEvent.cartUpdated" onEvent="${handleCartUpdate}">
<Text data="Items in cart: ${toString(cartCount)}"/>
</EventListener>
</Controller>
In this pattern, the onEvent callback updates the dependencies before the children
rebuild, making the new values available to the child elements.
Listening for Multiple Events
Nest separate listeners for different events:
<Column>
<EventListener event="AppEvent.userLoggedIn">
<Text data="Welcome back!"/>
</EventListener>
<EventListener event="AppEvent.cartUpdated" onEvent="${handleCartUpdate}">
<Text data="Items: ${toString(cartCount)}"/>
</EventListener>
</Column>
Listening for Events in Dart
Mix in EventNotifier and use addListener() / removeListener():
class DashboardController extends Controller with EventNotifier {
@override
dynamic init() {
addListener(AppEvent.dataRefreshed, _onDataRefreshed);
}
void _onDataRefreshed(Enum event, dynamic payload) {
// Process the event and payload
setState(() {});
}
@override
void dispose() {
removeListener(AppEvent.dataRefreshed, _onDataRefreshed);
super.dispose();
}
}
Important
Always remove listeners in dispose() to prevent memory leaks.
When to Use Events vs. Dependencies
Events are best suited for loosely-coupled communication between components that don't have a direct parent-child relationship — for example, notifying unrelated UI sections of state changes, broadcasting authentication state, or triggering refreshes across multiple controllers.
For parent-child data flow within a single fragment, use
Dependencies with <ValueListener> instead.