Skip to content

Controllers

XWidget allows you to define and register custom controllers to manage business logic and dynamic behaviors within your fragments. Controllers act as the bridge between your fragments and the underlying data or event handling mechanisms.

Creating Controllers

Define controllers in lib/xwidget/controllers/:

import 'package:xwidget/xwidget.dart';

class CounterController extends Controller {
  var count = 0;

  @override
  void bindDependencies() {
    dependencies.setValue("count", count);
    dependencies.setValue("increment", increment);
  }

  void increment() {
    dependencies.setValue("count", ++count);
  }
}

Generating Controller Bindings

To generate controller bindings, run the following command:

$ dart run xwidget_builder:generate --only controllers

This command processes your project’s controller definitions and generates the necessary Dart files to integrate them into your application.

Registering Controllers in Your Application

Once the controllers have been generated, you need to register them during your app’s initialization. Update your main function as follows:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  ...
  // register XWidget components
  registerXWidgetInflaters();
  registerXWidgetControllers();
  ...
}

Using Controllers in Your Fragments

Bind controllers to UI elements:

<Controller name="CounterController">
    <!-- listen for changes to 'count' and update children -->
    <ValueListener varName="count">
        <Text data="${toString(count)}"/>
    </ValueListener>
    <Button onPressed="${increment}">
        <Text>Increment</Text>
    </Button>
</Controller>

Controller Lifecycle

  1. Creation: Controller instance is created via registered factory
  2. Initialization: init() method is called (can be async)
  3. Dependency Binding: bindDependencies() is called
  4. Child Inflation: Child widgets are inflated with access to dependencies
  5. Build: UI is rendered with controller data

Creating a Controller

Controllers must extend the Controller base class and can override two methods:

init() Method

Called once when the controller is created. Can return: - void for synchronous initialization - Future for asynchronous initialization (shows progress widget) - Stream for streaming data

class MyController extends Controller {
  @override
  Future<void> init() async {
    // Async initialization
    await loadData();
  }
}

bindDependencies() Method

Called after init() completes. Use this to expose data to the XML markup:

class MyController extends Controller {
  List<Product> products = [];

  @override
  Future<void> init() async {
    products = await api.fetchProducts();
  }

  @override
  void bindDependencies() {
    dependencies['products'] = products;
    dependencies['totalCount'] = products.length;
    dependencies['refresh'] = refresh; // Expose method
  }

  Future<void> refresh() async {
    products = await api.fetchProducts();
    setState(() {}); // Rebuild UI
  }
}

Registering Controllers

Controllers must be registered before use:

void main() {
  // Register controller factories
  XWidget.registerController('ProductsController', () => ProductsController());
  XWidget.registerController('UserController', () => UserController());

  runApp(MyApp());
}