美文网首页Flutterflutter
Flutter Notification使用说明

Flutter Notification使用说明

作者: oceanLong | 来源:发表于2020-02-23 22:12 被阅读0次

    Flutter Notification 使用说明

    概述

    在Flutter进行界面开发时,我们经常会遇到数据传递的问题。由于Flutter采用节点树的方式组织页面,以致于一个普通页面的节点层级会很深。当我们需要在子节点向父节点传递一些信息时,我们不可能层层传递Listener,所以我们需要一种在子节点跨层级传递消息的方式。

    所幸,Flutter的Notification为我们提供了这样的能力。

    使用方法

    创建Notification

    class TestNotification extends Notification {
      TestNotification({
        @required this.count,
      });
    
      final int count;
    }
    

    我们在Notification中定义我们要传递的信息,本例中,我们只传递一个Int型。

    创建NotificationListener节点

    NotificationListener继承了StatelessWidget。我们在一个较高的父节点,使用NotificationListener,就可以监听来自子节点的消息了。

    class GrandParentWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: new AppBar(
            title: new Text('Notification Demo'),
          ),
          body: NotificationListener<TestNotification>(
            child: ParentWidget(),
            onNotification: (TestNotification n){
              print('随机数:${n.count}');
              return true;
            },
          ),
        );
      }
    }
    

    发送Notification

    class ButtonWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Center(
          child: GestureDetector(
            child: Text("Click Me"),
            onTap: () {
              new TestNotification(count: new Random().nextInt(100))
                  .dispatch(context);
            },
          ),
        );
      }
    }
    
    

    原理

    Notification

    我们先来看看Notification的实现。Notification的实现非常简单,阅读源码时实属福利。

    /// A notification that can bubble up the widget tree.
    ///
    /// You can determine the type of a notification using the `is` operator to
    /// check the [runtimeType] of the notification.
    ///
    /// To listen for notifications in a subtree, use a [NotificationListener].
    ///
    /// To send a notification, call [dispatch] on the notification you wish to
    /// send. The notification will be delivered to any [NotificationListener]
    /// widgets with the appropriate type parameters that are ancestors of the given
    /// [BuildContext].
    abstract class Notification {
      /// Abstract const constructor. This constructor enables subclasses to provide
      /// const constructors so that they can be used in const expressions.
      const Notification();
    
      /// Applied to each ancestor of the [dispatch] target.
      ///
      /// The [Notification] class implementation of this method dispatches the
      /// given [Notification] to each ancestor [NotificationListener] widget.
      ///
      /// Subclasses can override this to apply additional filtering or to update
      /// the notification as it is bubbled (for example, increasing a `depth` field
      /// for each ancestor of a particular type).
      @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;
      }
    
      /// Start bubbling this notification at the given build context.
      ///
      /// The notification will be delivered to any [NotificationListener] widgets
      /// with the appropriate type parameters that are ancestors of the given
      /// [BuildContext]. If the [BuildContext] is null, the notification is not
      /// dispatched.
      void dispatch(BuildContext target) {
        // The `target` may be null if the subtree the notification is supposed to be
        // dispatched in is in the process of being disposed.
        target?.visitAncestorElements(visitAncestor);
      }
    
        ...
    }
    
    

    除了注释之外,Notification的核心代码只有十行左右。主要包含了visitAncestordispatch两个方法。

    我们在调用dispatch后,会调用visitAncestorElements

      /// Walks the ancestor chain, starting with the parent of this build context's
      /// widget, invoking the argument for each ancestor. The callback is given a
      /// reference to the ancestor widget's corresponding [Element] object. The
      /// walk stops when it reaches the root widget or when the callback returns
      /// false. The callback must not return null.
      ///
      /// This is useful for inspecting the widget tree.
      ///
      /// Calling this method is relatively expensive (O(N) in the depth of the tree).
      ///
      /// This method should not be called from [State.deactivate] or [State.dispose]
      /// because the element tree is no longer stable at that time. To refer to
      /// an ancestor from one of those methods, save a reference to the ancestor
      /// by calling [visitAncestorElements] in [State.didChangeDependencies].
      void visitAncestorElements(bool visitor(Element element)) {
        assert(_debugCheckStateIsActiveForAncestorLookup());
        Element ancestor = _parent;
        while (ancestor != null && visitor(ancestor))
          ancestor = ancestor._parent;
      }
    
      
    

    visitAncestorElements是framework.dart中的方法,从注释中我们可以比较容易理解,这个方法主要是Flutter为我们提供的Widget向上遍历的方法。在调用方法时,我们需要传入一个visitor方法,当visitor方法返回false时,遍历终止。

    到这里,我们就明白了,Notification的dispatch方法,其实是向上遍历,寻找符合条件的父节点,然后进行处理。接下来我们看下Notificationvisitor

     /// Applied to each ancestor of the [dispatch] target.
      ///
      /// The [Notification] class implementation of this method dispatches the
      /// given [Notification] to each ancestor [NotificationListener] widget.
      ///
      /// Subclasses can override this to apply additional filtering or to update
      /// the notification as it is bubbled (for example, increasing a `depth` field
      /// for each ancestor of a particular type).
      @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;
      }
    

    实现非常简单,就是判断element的widget是否为NotificationListener,然后进行分发。如果分发的返回true,则visitAncestor返回false,遍历终止。

    在使用时,我们可以重写visitAncestor方法,来修改遍历的检查判断。

    NotificationListener

    将下来,我们看一下NotificationListener的实现。

    /// A widget that listens for [Notification]s bubbling up the tree.
    ///
    /// Notifications will trigger the [onNotification] callback only if their
    /// [runtimeType] is a subtype of `T`.
    ///
    /// To dispatch notifications, use the [Notification.dispatch] method.
    class NotificationListener<T extends Notification> extends StatelessWidget {
      /// Creates a widget that listens for notifications.
      const NotificationListener({
        Key key,
        @required this.child,
        this.onNotification,
      }) : super(key: key);
    
      /// The widget directly below this widget in the tree.
      ///
      /// This is not necessarily the widget that dispatched the notification.
      ///
      /// {@macro flutter.widgets.child}
      final Widget child;
    
      /// Called when a notification of the appropriate type arrives at this
      /// location in the tree.
      ///
      /// Return true to cancel the notification bubbling. Return false (or null) to
      /// allow the notification to continue to be dispatched to further ancestors.
      ///
      /// The notification's [Notification.visitAncestor] method is called for each
      /// ancestor, and invokes this callback as appropriate.
      ///
      /// Notifications vary in terms of when they are dispatched. There are two
      /// main possibilities: dispatch between frames, and dispatch during layout.
      ///
      /// For notifications that dispatch during layout, such as those that inherit
      /// from [LayoutChangedNotification], it is too late to call [State.setState]
      /// in response to the notification (as layout is currently happening in a
      /// descendant, by definition, since notifications bubble up the tree). For
      /// widgets that depend on layout, consider a [LayoutBuilder] instead.
      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;
      }
    
      @override
      Widget build(BuildContext context) => child;
    }
    

    我们可以看到,dispatch方法,只是进行了一些简单的类型检查,然后就调用我们传入的notification方法了。这里值得注意的是,只有当我们notification返回true时,遍历才会终止。

    以上就是Flutter中Notification的基本原理和使用方法。

    相关文章

      网友评论

        本文标题:Flutter Notification使用说明

        本文链接:https://www.haomeiwen.com/subject/xoreqhtx.html