StatefulWidget Binder

A lightweight, type-safe Flutter package to bind controllers to StatefulWidgets with automatic lifecycle management and reactivity.

Features

  • ๐ŸŽฏ Type-Safe: Full type-safe access to your controller within the State class.
  • ๐Ÿ”„ Reactive: Automatically rebuilds when your controller notifies changes (if it's Listenable).
  • ๐Ÿงน Clean Lifecycle: Automatically disposes controllers when widgets are removed from the tree.
  • ๐Ÿ”’ Encapsulated: Keeps core implementation details clean and private.

Getting started

Add stateful_widget_binder to your pubspec.yaml:

dependencies:
  stateful_widget_binder: ^1.0.2

Usage

1. Define your Controller

Extend ListenableController for a fully reactive experience. The UI will automatically rebuild when notifyListeners() is called.

class CounterController extends ListenableController {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // Triggers UI update
  }
}

Option B: Pure Disposable Controller

Implement or extend DisposableController if you don't need automatic UI rebuilds (e.g., for background services, loggers, or manually managed state).

class LogManager extends DisposableController {
  @override
  void dispose() {
    print('Cleaning up resources...');
  }
}

2. Create your Widget and State

Simply extend StatefulWidgetBinder for the widget and StateController for the state:

class CounterWidget extends StatefulWidgetBinder {
  // Pass the controller through the constructor to the super class
  const CounterWidget({super.key, required super.controller});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

// Specify the Widget and Controller types for type-safe access
class _CounterWidgetState extends StateController<CounterWidget, CounterController> {
  @override
  Widget build(BuildContext context) {
    // The 'controller' getter is already typed as CounterController
    return Scaffold(
      body: Center(child: Text('Count: ${controller.count}')),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Advanced Options

You can tune the lifecycle and reactivity behavior directly through the constructor:

CounterWidget(
  controller: myController,
  disposeController: false, // Default is true. Prevents calling controller.dispose() automatically.
  rebuildOnNotify: false,    // Default is true. Stops the UI from rebuilding on controller updates.
)

Additional information

This package is designed for developers who want a lightweight way to separate business logic from the UI without the overhead of heavy frameworks. It bridges the gap between pure StatefulWidget and high-level state management libraries.