+593 99 022 3684 dime@hassler.ec

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.


NotificationListener

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.

notifications bubble up

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.

class TimeNotification extends Notification {
  final String time;

  const TimeNotification({this.time});
}

Now let´s create a simple widget made from a Button that will trigger one of the described notifications when pressed.

class Timer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FlatButton(
      child: Text("Get time!"),
      onPressed: () {
        final time = DateTime.now().toIso8601String();
        TimeNotification(time: time)..dispatch(context);
      },
    );
  }
}

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.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("NotificationListener example"),
      ),
      body: NotificationListener<TimeNotification>(
          child: Timer(),
          onNotification: (notification) {
            print(notification.time);
          }),
    );
  }
}

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.


ValueListenableBuilder

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.

Values go down

Create the widget in which we want the event to be received.

class CounterText extends StatelessWidget {
  final ValueListenable<int> _counter;

  CounterText(this._counter);

  @override
  Widget build(BuildContext context) {
    return  ValueListenableBuilder<int>(
        valueListenable: _counter,
        builder: (context, value, child) {
          return new Text(value.toString());
        });
  }
}

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.

class MyHomePage extends StatelessWidget {
  final _counter = new ValueNotifier<int>(0);
}

Then, implement build method.

@override
Widget build(BuildContext context) {
  return Scaffold(
      appBar: AppBar(
        title: Text("ValueListenableBuilder example"),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('You have pushed the button this many times:'),
              CounterText(_counter)
            ],
          ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _counter.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ));
}

CounterText widget needs _counter, which is a ValueNotifier / ValueListenable, to be injected.

CounterText(_counter)

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

class CounterListenableProvider extends InheritedWidget {
  final ValueListenable<int> counter;

  CounterListenableProvider({Key key, @required this.counter, Widget child})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }

  static ValueListenable<int> of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(CounterListenableProvider)
            as CounterListenableProvider)
        .counter;
  }
}

Add a ValueListenable<int> attribute and pass it to the constructor.

@required this.counter

Also, add a method to get that ValueListenable

static ValueListenable<int> of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(CounterListenableProvider)
            as CounterListenableProvider)
        .counter;
}

Let´s see how previous example may be rewritten using CounterListenableProvider.

Event receiver widget.

class CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return  ValueListenableBuilder<int>(
        valueListenable: CounterListenableProvider.of(context),
        builder: (context, value, child) {
          return new Text(value.toString());
        });
  }
}

CounterListenableProvider is now providing ValueListenable. Now it´s time to create CounterListenableProvider.

class MyHomePage extends StatelessWidget {
  final _counter = new ValueNotifier(0);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ValueListenableBuilder example"),
        ),
        body: CounterListenableProvider(
          counter: _counter,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                CounterText()
              ],
            ),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => _counter.value++,
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ));
  }
}

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.

 

 

 

SOURCE:

WRITTEN BY

Go to the profile of Chema Rubio

Chema Rubio

Android developer @BBVANext // @develodroid

Flutter Community

Flutter Community

Articles and Stories from the Flutter Community

Si continuas utilizando este sitio aceptas el uso de cookies. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar