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 Notification
s which are listened to by NotificationListener
widgets. This tutorial explains how it works with examples.
Create NotificationListener
NotificationListener
is a widget that listens for Notification
s 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.