> For the complete documentation index, see [llms.txt](https://wong-coupon.gitbook.io/flutter/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wong-coupon.gitbook.io/flutter/state-management/changenotifier.md).

# ChangeNotifier

## **When You Need to Control a Widget from Outside**

State management solutions are incredibly convenient, but sometimes we need **pure Flutter solutions** without relying on third-party libraries.

In some cases, we have **shared widgets** that require a **built-in state management tool** from Flutter itself. This is where **ChangeNotifier** becomes a powerful option.

We can think of **ChangeNotifier** as a way to create controllers similar to familiar built-in Flutter controllers like:

* [**TextEditingController**](https://api.flutter.dev/flutter/widgets/TextEditingController-class.html) – used to control text fields.
* [**ScrollController**](https://api.flutter.dev/flutter/widgets/ScrollController-class.html) – used to control scrolling behavior.

Just like these, **ChangeNotifier** allows us to create custom controllers for widgets while keeping everything within Flutter’s ecosystem.

## **Quickly Implementing ChangeNotifier**

Using **ChangeNotifier** requires initialization and writing a significant amount of code. To simplify this process, I use **Mason** to generate a **boilerplate template** for faster development.

If you're unfamiliar with Mason, check out this guide:

{% content-ref url="/pages/6Yh2LXIXAdzRX2g1isw5" %}
[Mason](/flutter/easy-code/mason.md)
{% endcontent-ref %}

You can also explore my **Mason template for ChangeNotifier** here:\
🔗 [dr\_change\_notifier](https://brickhub.dev/bricks/dr_change_notifier)

### **Controller Section**

This part defines the **controller** responsible for managing the widget’s state.

```dart
/// Part II: Controller
class {{name.pascalCase()}}Controller with ChangeNotifier implements Listenable {
  /// Learn more: url_to_document_of_package

  int _example = 0;
  int get example => _example;

  // USE | controller.example = example;
  set example(int index){
    _example = index;
    notifyListeners();
  }

  //  @override
  //   void dispose() {
  //   super.dispose();
  //
  // }
}
```

This section contains variables for storing data. If you want to update the screen that uses this controller, call **`notifyListeners();`** at the end of the function. This will trigger a **UI rebuild**.

### **UI Section**

This part defines how the **user interface (UI)** interacts with the **ChangeNotifier** controller.

```dart
/// Part I: Screen
class {{name.pascalCase()}} extends StatefulWidget {
  const {{name.pascalCase()}}({super.key, this.controller, this.autoDispose = true});

  final  {{name.pascalCase()}}Controller? controller;
  final bool autoDispose;

  @override
  State<{{name.pascalCase()}}> createState() => _{{name.pascalCase()}}State();
}

class _{{name.pascalCase()}}State extends State<{{name.pascalCase()}}> {

  late {{name.pascalCase()}}Controller controller;

  @override
  void initState() {
    super.initState();
    controller = widget.controller ?? {{name.pascalCase()}}Controller();
    controller.addListener(() {
      if (mounted) {
        setState(() {});
      }
    });
  }

  // update once properties is changed
  @override
  void didUpdateWidget({{name.pascalCase()}} oldWidget) {
    super.didUpdateWidget(oldWidget);
    // if (widget.path != oldWidget.path) {
    //
    // }
  }

  @override
    void dispose() {
    if(widget.autoDispose) controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

    // final ratio = context.dsgRatio;

    return const Text("ok");
  }
}

```

In this UI section, I have pre-configured the following:

* **Automatic controller initialization** if no controller is passed in `initState()`.
* **Automatic disposal** of the controller when the widget is destroyed in `dispose()`.
* **Listening for `notifyListeners()`** calls from the controller and updating the UI accordingly.

```dart
    controller.addListener(() {
      if (mounted) {
        setState(() {});
      }
    });
```

## **Real-World Usage**

<div data-full-width="false"><figure><img src="/files/xC0xffMXoKjFDQrBJYji" alt="" width="563"><figcaption><p>Real-World Usage</p></figcaption></figure></div>

Assuming I have a **ChangeNotifier structure for the camera** as shown in the image, the initialization process will be as follows:

#### **Step 1: Initialize the Controller**

```dart
CameraWidgetController controllerCamera = CameraWidgetController();
```

Make sure to **dispose of it** when it's no longer needed.

***

#### **Step 2: Initialize the UI**

<figure><img src="/files/HyFtwyM5n2fLRMw8H286" alt=""><figcaption><p>Initialize the UI</p></figcaption></figure>

Pass the **controller** into the widget that needs to be controlled externally.

#### **Step 3: Usage**

<figure><img src="/files/80sjwdqqRZvGWBcYo3lN" alt=""><figcaption><p>Step 3: Usage</p></figcaption></figure>

Now, you can use it **anywhere** in your project as needed.

This is my **approach to controlling widgets externally**. You can use this as a reference and create your **own code template with Mason**, customizing it to fit your coding style and preferences.

[Buy Me a Coffee](https://buymeacoffee.com/ducmng12g) | [Support Me on Ko-fi](https://ko-fi.com/I2I81AEJG8)
