0%

Flutter Notification源码浅读

Notification

NotificationListener也是冒泡传递,和触摸Listener不同的是:NotificationListener可以通过onNotification阻止继续冒泡,而Listener不行。

通知使用流程

通知类,每个节点都可以发送通知Notification,每个节点也都可以接收通知。通知通过冒泡逐级往父类查找对于泛型的NotificationListener监听。

下面我们先制定一个通知类继承Notification, 走一遍通知流程:

自定义通知类

1
2
3
4
5
6
7
import 'package:flutter/material.dart';

class RZNotification extends Notification {
final int count;
RZNotification({required this.count}) : super();
}

发送通知

通过通知实例,调用dispatch方法,dispatch方法默认在抽象类Notification实现

1
2
RZNotification notification = RZNotification(count: 5);
notification.dispatch(context);

监听通知

1
2
3
4
5
6
7
NotificationListener<RZNotification>(
onNotification: (RZNotification rzNotification) {
print('===rzNotification==00=${rzNotification.count}');
return true;
},
child:

NotificationListener

监听类只有两个成员child和onNotification,一个私有方法(_dispatch),一个build方法,build方法直接返回传入child。

_dispatch

通知类通过element树查找所有通知监听类NotificationListener ,调用监听类的_dispatch,并传入通知类实例。_dispatch 方法校验是否属于自己监听的泛型类,如果是并且onNotification方法返回为true。

接收Notification对应泛型子类的通知,对于泛型子类通过dispatch(BuildContent? target)方法——通过element树层层往上级查找,找到对应泛型的Listener,即调用onNotification回调。

再看Notification

dispatch(BuildContent? target)

Notification类为抽象类,子类直接调用dispatch方法,通过传入BuildContentvisitAncestorElements查找,并传入查找逻辑(visitor)

visitAncestorElements是BuildContent实例方法:

1
2
3
4
5
6
7
@override
void visitAncestorElements(bool Function(Element element) visitor) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element? ancestor = _parent;
while (ancestor != null && visitor(ancestor))
ancestor = ancestor._parent;
}

个人理解是传入一个访问逻辑,visitAncestorElements逐级往上查找父类——do循环,如果父类不为空,将父类传入访问逻辑中,如果访问逻辑返回true就继续查找改父类的父类,如果返回false,就停止查找。

visitAncestor

访问逻辑visitAncestor,并不知道是那个子类调用的dispatch方法,所以先查找widget is NotificationListener<Notification>,是的话就调用NotificationListener的_dispatch方法,并传入调用者,NotificationListener都知道自己监听的泛型类是什么,如果是调用者是自己监听的类,即调用onNotification回调。

1
2
3
4
5
6
7
8
9
10
11
12
@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;
}

总结

通过NotificationListener和Notification配合,我们清晰知道了通知发送和接收的逻辑。通知这种方式的通信也有它的局限性,监听者只能监听其子widget发送的通知。所以我们也需要根据应用场景实际情况选择性去使用。

并且熟悉了Context和Element关系:

  • Context遍历Element树的方法
  • 可以通过Element.widget得到element节点对应的widget