The introduction

  • Reference: The Practice of Flutter (by Du Wen) – Section 8.4 Notification. This series of articles are very good, recommended!
  • For those familiar with Notification, please refer directly to the bubbling Principles section.

Notification

Notification is an important mechanism in Flutter. Notifications are distributed at any node in the Widget tree. Notifications bubble up along the current node.

Notifications are used in many parts of the Flutter. For example, a Scrollable Widget sends a ScrollNotification when it slides. The Scrollbar listens to the ScrollNotification to determine the position of the Scrollbar.

class NotificationPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NotificationListener<ScrollEndNotification>(
        onNotification: (notification){
          switch (notification.runtimeType){
            case ScrollStartNotification: print("Start rolling"); break;
            case ScrollUpdateNotification: print("Rolling"); break;
            case ScrollEndNotification: print("Roll stop"); break;
            case OverscrollNotification: print("Scroll to the boundary"); break;
            case UserScrollNotification: print("Scroll to the boundary"); break;
            default:
              print(notification.runtimeType);
              break;
          }
          return true;
        },
        child: ListView.builder(
            itemCount: 20,
            itemBuilder: (_, index) {
              return ListTile(title: Text('$index')); }),),); }}Copy the code

At line 17, if true is returned in the onNotification callback method of NotificationListener, the current node intercepts the notification message and prevents upward transmission; Returning false indicates no interception.

Custom notification

class xxxNotification extends Notification {
  final String msg;

  xxxNotification(this.msg);
}

NotificationListener<xxxNotification>(
	onNotification: (notification) {
 		 return false or true;
  	 },
   	child:
   	 ...
   		 xxxNotification(' Hello ').dispatch(context); ...). ;Copy the code
  • The UI control is wrapped with a NotificationListener (functional Widget) where it needs to listen for notifications, and then a child control sends notifications using the xxxNotification().dispatch(context) method.

  • Notification’s dispatch method has the bubbling principle we want to understand.

Notice the bubbling principle

1. The child node uses the Notification. Dispatch (context) method to distribute notifications

[./notification_listener.dart]

  voiddispatch(BuildContext target) { target? .visitAncestorElements(visitAncestor); }Copy the code
  • The notification. dispatch(context) method actually calls context.visitancestorelements () and passes in a Function().bool visitor(Element element)) parameters.

[./framework.dart]

  @override
  void visitAncestorElements(bool visitor(Element element)) {
    // ...
    Element ancestor = _parent;
    while(ancestor ! =null && visitor(ancestor))
      ancestor = ancestor._parent;
  }
Copy the code
  • Context. VisitAncestorElements () eventually found in the Element type of the corresponding implementation (as for the context – the relationship between the Element are interested can go to study).

  • Lines 4-6, a typical recursive loop. Start at the current Element’s parent and iterate up through the parent, calling the visitor() method to check the parent.

  • There are two ways to break out of the loop:

    1. No parent node (ancestor == null) : Already walked from the current element to the root element

    2. Method visitor(ancestor) returns false: The method that checks the parent node returns false.

  • The key to the bubbling principle is this method bool visitor(Element Element).

2. Method of Notification class

[./notification_listener.dart]

  voiddispatch(BuildContext target) { target? .visitAncestorElements(visitAncestor); }@protected
  @mustCallSuper
  bool visitAncestor(Element element) {
    if (element is StatelessElement) {
      final StatelessWidget widget = element.widget;
      if (widget is NotificationListener<Notification>) {
        if (widget._dispatch(this, element)) // that function checks the type dynamically
          return false; }}return true;
  }
Copy the code
  • At line 8, check whether element is a StatelessElement. We do this because we typically wrap the Child control with a NotificationListener, which is the control inherited from the StatelessWidget, to listen for notifications issued by the child node.

  • Lines 8-10 determine whether the method entry element is a NotificationListener. In connection with the previous section, you can see that during the context traversal of the parent node, the visitor method is used to determine whether the parent node is NotificationListener; if so, its _dispatch() method is called.

Note here:

  1. If the parent node is NotificationListener and the _dispatch() method returns true, the visitor() method returns false, interrupting the loop loop (see the code section for breaking out of the loop condition).

  2. If the parent node is not a NotificationListener or the _dispatch() method returns false, the visitor() method returns true and continues the loop up through the parent node.

NotificationListener class _dispatch method

[./notification_listener.dart]

final NotificationListenerCallback<T> onNotification;
 
bool _dispatch(Notification notification, Element element) {
    if(onNotification ! =null && notification is T) {
      final bool result = onNotification(notification);
      return result == true; // so that null and false have the same effect
    }
    return false;
  }
Copy the code
  • Line 5, callonNotification()Method, which is when we construct NotificationListener incoming to listen Notification callback (NotificationListenerCallback) (Notification). So if we want to intercept notifications, the callback method here should return true, and then_dispatch()Method returns true, breaking the loop loop.
NotificationListener<xxxNotification>(
	onNotification: (notification) {
 		 return false or true; // true intercepts notifications
  	 },
   	child:
   	
   		 xxxNotification(' Hello ').dispatch(context); ) ;Copy the code

Notification & NotificationListener structure diagram

Conclusion under

XxxNotification Indicates a user-defined notification

1.xxxNotification.dispatch(context)When you distribute notifications, it’s calledcontext.visitAncestorElements(visitor)Methods.

2,context.visitAncestorElements()The () method is used to traverse the parent node from the current node to find oneNotificationListerType, and then call itsonNotification()Callback method.

3. The input parameter of the callback method is the notification from the child node (xxxNotification), and the return value of the callback method is used to determine whether to block the notification (xxxNotification).

Layer by layer upNotificationListerType parent and distribute notifications (xxxNotification); If not, continue up until you reach the root node. This is what we call the bubbling notification principle.

5. Finally, thanks again for the Flutter Practice series!