【Flutter核心类分析】深入理解数据共享Notification

背景

上一文深入理解InheritedWidget我们知道,InheritedWidget的数据共享方式是父Widget到子Widget的逐层传递,适用于父Widget状态变更通知子widget变更的场景;那么本文讲解的Notification正好相反了,数据的的流动方式就是从子Widget向上传递只父Widget,适用于子widget状态变化,通知上层做相应处理的场景。

Notification用法

Notification用法分为以下几个步骤;

  1. 自定义一个通知类,需要继承自Notification类;
class CustomNotification extends Notification {
  CustomNotification(this.msg);
  final String msg;
}
  1. 接收通知的父widget使用NotificationListener包装(NotificationListener实际上也是一个StatelessWidget),并实现onNotification方法
class _MyHomePageState extends State<MyHomePageWidget> {
  String _msg = " 通知:n";

  @override
  Widget build(BuildContext context) {
    // 监听通知
    return NotificationListener<CustomNotification>(
        onNotification: (notification) {
          setState(() {
            _msg += notification.msg;
          }); // 收到子 Widget 通知,更新 msg
          return false;
        },
        child: Container(
          color: Colors.white,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  _msg,
                  style: TextStyle(fontSize: 15),
                ),
                CustomChild()
              ], // 将子 Widget 加入到视图树中
            ),
          ),
        ));
  }
}

  1. 子widget调用Notification类的dispatch(context)方法(父widget的onNotification就会收到通知)。
// 抽离出一个子 Widget 用来发通知
class CustomChild extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      // 按钮点击时分发通知
      onPressed: () => CustomNotification("Hellon").dispatch(context),
      child: Text("Send"),
    );
  }
}

我们来看看效果,点击Button三次:

image-20211111101739610

不出意外,父Widget都能及时收到通知并刷新

下面我们一起通过源码来看看它的原理

原理

上面介绍了Notification的使用方法,现在我们深入其源码来了解一下它的实现原理。我们从通知的源头触发。通知是通过Notificationdispatch(context)方法发出的,那我们先看看dispatch(context)方法中做了什么,下面是相关源码:

  void dispatch(BuildContext? target) {
    target?.visitAncestorElements(visitAncestor);
  }

dispatch(context)中调用了当前context的visitAncestorElements方法,我们从深入理解BuildContext一文中知道,该方法作用是访问父级Elements。他的参数是一个回调方法。我们这里看看他的源码:

  @override
  void visitAncestorElements(bool visitor(Element element)) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    Element? ancestor = _parent;
    while (ancestor != null && visitor(ancestor))
      // 遍历父级element
      ancestor = ancestor._parent;
  }

它会遍历父级element,直到ancestor为null或者某个遍历回调方法返回false。

源码中的参数visitAncestor,就是Notification类中的visitAncestor方法。

// 每一个遍历到的父级Element执行此函数
bool visitAncestor(Element element) {
  //判断当前element对应的Widget是否是NotificationListener。
  //由于NotificationListener是继承自StatelessWidget,
  if (element is StatelessElement) {
    final StatelessWidget widget = element.widget;
    //是NotificationListener,则调用该NotificationListener的_dispatch方法
    if (widget is NotificationListener<Notification>) {
      if (widget._dispatch(this, element)) // that function checks the type dynamically
        return false;
    }
  }
  return true;
}

visitAncestor会判断每一个遍历到的父级Widget是否是NotificationListener,如果不是,那么久返回true,继续遍历,如果是,就调用NotificationListener的_dispatch方法,是否继续遍历同样是根据_dispatch方法的返回值来判断。

来看看NotificationListener的_dispatch

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;
}

我们可以看到,NotificationListeneronNotification回调最终是在_dispatch方法中执行的。是否继续向上遍历同样也是根据onNotification方法的返回值来判断,返回为true继续遍历,返回为false终止遍历。

到了这里整个Notification的流程就形成 了一个闭环。

总结一下

我们其实发现Notification的源码其实很简单,没什么复杂的逻辑。Notification的核心功能也就是实现了子Widget向父Widget传递消息。看源码同样也是支持多个NotificationListener嵌套通知实现。

Flutter中也有很多类似的场景比如,ScrollNotificationLayoutChangedNotification等等。。。。。。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>