Flutter - Using Notification & NotificationListener Examples

This tutorial shows you how to use Notification and NotificationListener in Flutter.

Flutter widgets are structured in a tree structure. Sometimes, an interaction or an update in a child widget may require the parent widget to perform a rebuild. In this case, the child widget needs to notify its parent. In Flutter, you can dispatch Notifications which are listened to by NotificationListener widgets. This tutorial explains how it works with examples.

Create NotificationListener

NotificationListener is a widget that listens for Notifications under its subtree. That means it can only receive notifications coming up from the widgets below its subtree.

  const NotificationListener({
    Key? key,
    required Widget child,
    bool Function(T)? onNotification,
  });

To use it, you have to pass a Widget as the child argument. Any widget under the subtree of child widget can send notifications which will be propagated up the tree.

Another important argument is onNotification. It allows you to pass a function that will be invoked when a notification is received.

When calling the constructor, you can optionally pass a parameterized type T. That will cause Flutter to only call the onNotification callback if the runtimeType is a subtype of T.

  NotificationListener<MyNotification>(
    child: Container(), // widget that can dispatch notifications
    onNotification: (MyNotification myNotification) {
      // Invoked when a notification of type MyNotification is received
    },
  )

If you want to receive notifications of all types, do not pass the parameterized type. As a result, you can make the parameter of the onNotification object to have Object? type.

  NotificationListener(
    child: Container(), // widget that can dispatch notifications
    onNotification: (Object? anyNotification) {
      // Invoked when any notification is received
    },
  )

Dispatch Notification

Flutter has a class named Notification. It can be used to create notifications that can bubble up the widget tree. You can create a code that dispatches a notification when a particular event or condition occurs. The dispatched notifications can be listened to by a NotificationListener. One of the implementations is the ScrollNotification, which is dispatched when a scroll event occurs.

To send a notification, you have to call the dispatch method of Notification. Since the Notification class is abstract, you need to have a class that extends Notification. Creating the class should be pretty simple since there is no method to be overridden. In the example below, we create a class with one field that holds a value to be sent to the listener.

  class MyNotification extends Notification {
  
    final int value;
  
    const MyNotification({required this.value});
  }

When you want to dispatch a notification, call the dispatch method by passing the BuildContext. In order for the notification to be received by the listener, the passed context must be under the NotificationListener widget in the tree.

    void dispatch(BuildContext? target)

For example, we want to create a button that generates a random value which is displayed by a Text widget. The button has to notify the NotificationListener by dispatching a notification. The below example doesn't work because it passes the BuildContext of the MyExample widget which is above the NotificationListener in the tree.

  class _MyExampleState extends State<MyExample> {
  
    int _value = 0;
  
    @override
    Widget build(BuildContext context) {
      return Padding(
        padding: const EdgeInsets.all(0.0),
        child: NotificationListener<MyNotification>(
          child: SizedBox(
            width: double.infinity,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('$_value'),
                OutlinedButton(
                  onPressed: () {
                    MyNotification(value: Random().nextInt(100)).dispatch(context);
                  },
                  child: const Text('Get Random Value'),
                ),
              ],
            ),
          ),
          onNotification: (MyNotification myNotification) {
            setState(() {
              _value = myNotification.value;
            });
            return true;
          },
        ),
      );
    }
  }

An easy solution is by creating another widget for the button. In the example below, by creating a new widget for the button, the passed BuildContext is now the one owned by the MyButton widget which is below the NotificationListener in the tree.

  class _MyExampleState extends State<MyExample> {
  
    int _value = 0;
  
    @override
    Widget build(BuildContext context) {
      return Padding(
        padding: const EdgeInsets.all(0.0),
        child: NotificationListener<MyNotification>(
          child: SizedBox(
            width: double.infinity,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('$_value'),
                const MyButton(),
              ],
            ),
          ),
          onNotification: (MyNotification myNotification) {
            setState(() {
              _value = myNotification.value;
            });
            return true;
          },
        ),
      );
    }
  }

  class MyButton extends StatelessWidget {

    const MyButton({super.key});

    @override
    Widget build(BuildContext context) {
      return OutlinedButton(
        onPressed: () {
          MyNotification(value: Random().nextInt(100)).dispatch(context);
        },
        child: const Text('Get Random Value'),
      );
    }
  }

If you don't want to create a widget, you can use the Builder widget.

Summary

Flutter allows you to dispatch Notification objects that can bubble up the widget tree. They can be listened to by NotificationListener widgets, which allow you to pass a callback to be invoked when a notification is received. This concept can be used for cases where a child widget needs to notify its ancestor widget that a certain event has occurred.

You can also read about.