专栏首页移动开发面面观Flutter Notification使用说明

Flutter Notification使用说明

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的基本原理和使用方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • LeakCanary源码解析

    LeakCanary是Android开发中非常常用的一个内存泄漏监测和分析工具。了解其工作原理,有助于对Android的内存泄漏有更深层次的认识。

    Oceanlong
  • InheritedWidget使用说明

    在Flutter进行界面开发时,我们经常会遇到数据传递的问题。由于Flutter采用节点树的方式组织页面,以致于一个普通页面的节点层级会很深。此时,我们如果还是...

    Oceanlong
  • 快速排序法及优化

    Oceanlong
  • textgenrnn 文本生成实战

    文本生成是一件很神奇的自然语言处理任务,深度学习给文本生成带来的全新的技术途径,如这篇文章The Unreasonable Effectiveness of R...

    sparkexpert
  • Tomcat 9访问 Host Manager

    由于机器环境是win7 旗舰版 64位,所以选择下载64-bit windows .zip。

    拓荒者
  • 使用社交媒体上粉丝的帖子来衡量品牌之间的相似性(Social and Information Networks)

    在这篇论文中,我们提出了一种新的测量方法,通过社交网络服务(SNS)上的品牌追随者的帖子来估计品牌之间的相似性。我们的方法是为了探索客户可能共同购买的品牌而开发...

    用户6869393
  • Android 相机功能模块相关库

    Android’s video recording APIs are very difficult to figure out, especially sinc...

    用户3004328
  • Ignatius and the Princess II

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Oth...

    用户2965768
  • 重设3COM-3C857 ADSL密码的

    公司有台3COM-3C857 的ADSL ,现在忘记了管理登陆密码,无法登陆管理了,想要重新Reset下,找了半天都没有发现重设按钮,原本这种老古董应该换新的了...

    py3study
  • 聊聊flink KeyedStream的reduce操作

    flink-streaming-java_2.11-1.7.0-sources.jar!/org/apache/flink/streaming/api/data...

    codecraft

扫码关注云+社区

领取腾讯云代金券