There are a couple of ways of communicating widgets in Flutter. In this post, we will check two of them: notifications and values. This means, we will review NotificationListener and ValueListenableBuilder Flutter provided widgets.
If we want to enable communications from all descendants widgets to their parent, NotificationListener may be used.
This widget may be useful, i.e. when listening to scroll progress in a ListView, GridView or any widget taking scroll.
¿Until which descendant notifications are listenable?
Communications implemented using notifications are not just about a parent listening notifications from their direct children. Notifications may be sent from all descendant layers.
Let´s see how notifications are implemented. First thing to be done, is creating the notification itself. In this case, a notification carrying the current time, will be sent by the time a button is pressed.
To do so, Notification class must be extended.
Now let´s create a simple widget made from a Button that will trigger one of the described notifications when pressed.
Create the notification, then call dispatch method. This method will make the notification to bubble up, it climbs up the widgets tree.
You may see the notification creation process similar to make soap bubbles and let them flow up in the air.
Now, we need a way to listen that bubble. It is time to use NotificationListener widget.
We have an Scaffold, its body in this case will be a NotificationListener. Taking the notification type into account (TimeNotification) it is important, this allows us just listening this specific notifications and discarding others.
Child attribute is used to continue growing the widget tree and onNotification attribute in order to listen all notifications created by the Notification listener descendants. Any child, grandchild and any lower level sending notifications will sink to onNotification method.
Other scenarios will need communications to be implemented inversely. This means from a parent widget to its descendants. This is possible thanks to ValueNotifier and ValueListenableBuilder widgets.
ValueListenableBuilder may be useful when performing animations due to its power to communicate the progress of an animation triggered from a widget to its descendants.
Create the widget in which we want the event to be received.
ValueListenable, allows us listening events triggered from ascendant widgets. On the other hand ValueListenableBuilder, feels similar to the NotificationListener previously reviewed. In this case, we want the widget to be built by the time an event takes place.
These events are offered in this example by ValueListenable, it must be used so it may be in charge of listening to events and calling the builder when events take place.
Now, create a new widget, it will parent the previous one and will be in charge of triggering events. We need to add a new ValueNotifier attribute, it will send events to its descendants, in this case our events will be carrying int type values.
Then, implement build method.
CounterText widget needs _counter, which is a ValueNotifier / ValueListenable, to be injected.
Last thing, add a FloatingActionButton to increment _counter, this will trigger an event that will be catched by our CounterText listening.
onPressed: () => _counter.value++,
ValueListenableBuilder & InheritedWidget
Remember CounterText(_counter) ? Better solution for ValueListenableconstructor passing, is using InheritedWidget.
To do so, create an InheritedWidget
Add a ValueListenable<int> attribute and pass it to the constructor.
Also, add a method to get that ValueListenable
Let´s see how previous example may be rewritten using CounterListenableProvider.
Event receiver widget.
CounterListenableProvider is now providing ValueListenable. Now it´s time to create CounterListenableProvider.
Wrap up the previous body using a CounterListenableProvider.
Now CounterText() does not take any ValueListenable as parameter. CounterListenableProvider.of(context) could be used in any CounterListenableProvider child´s descendants.
This approach makes building the widget tree easier because sharing ValueListenable is now provided by CounterListenableProvider.
To sum up
In this post it was explained how to send notifications and values all along the UI widget tree.
Key concepts in here are:
- Communications from lower level widgets to upper levels are implemented using Notifications and Notifications Listeners.
- The opposite communication way, should be implemented using ValueNotifiers and ValueListenableBuilders. This last may be improved using InheritedWidget allowing us to share a ValueListenable.
Articles and Stories from the Flutter Community