Skip to content

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.