Flutter Notification 自底向上的数据传递
Flutter Notification 自底向上的数据传递
Notification和InheritedWidget作用相反,InheritedWidget是自上向下传递数据,Notification是自下向上传递数据.
NotificationListener
NotificationListener
的代码很简单,只有一个方法,onNotification
,处理通知事件,并通过返回布尔值来决定该事件是否继续向上传递,这个方法需要用户自定义.
onNotification
final NotificationListenerCallback<T>? onNotification;
Notification
Notification
也只有一个方法dispatch
.
dispatch
的方法很简单,就是调用传入BuildContext的dispatchNotification
方法.
void dispatch(BuildContext? target) {
target?.dispatchNotification(this);
}
下面看一下dispatchNotification
方法.它是调用了_notificationTree
的dispatchNotification
方法。
@override
void dispatchNotification(Notification notification) {
_notificationTree?.dispatchNotification(notification);
}
_notificationTree
是每个element
都有的一个对象,这里看一下它的初始化。正常是赋值了父element的_notificationTree
对象.
void attachNotificationTree() {
_notificationTree = _parent?._notificationTree;
}
但是要是element
添加了NotifiableElementMixin
mixin,它就会创建一个新的_NotificationNode
mixin NotifiableElementMixin on Element {
bool onNotification(Notification notification);
@override
void attachNotificationTree() {
_notificationTree = _NotificationNode(_parent?._notificationTree, this);
}
}
而NotificationListener
组件的element
就添加了这个NotifiableElementMixin
mixin
const NotificationListener({
super.key,
required super.child,
this.onNotification,
});
final NotificationListenerCallback<T>? onNotification;
@override
Element createElement() {
return _NotificationElement<T>(this);
}
}
class _NotificationElement<T extends Notification> extends ProxyElement with NotifiableElementMixin {
_NotificationElement(NotificationListener<T> super.widget);
@override
bool onNotification(Notification notification) {
final NotificationListener<T> listener = widget as NotificationListener<T>;
if (listener.onNotification != null && notification is T) {
return listener.onNotification!(notification);
}
return false;
}
@override
void notifyClients(covariant ProxyWidget oldWidget) {
// Notification tree does not need to notify clients.
}
}
从这里我们可以推断出每一层NotificationListener
它都有一个属于自己的_NotificationNode
它的作用就是可以判断是否要继续向上通知。看到这里我们应该明白了_notificationTree
是怎么生成的,我们再来看一下它的是怎么向上通知的。_notificationTree
会调用自身的dispatchNotification
方法,这个方法会调用onNotification
方法。
void dispatchNotification(Notification notification) {
if (current?.onNotification(notification) ?? true) {
return;
}
parent?.dispatchNotification(notification);
}
而这里它则是调用了上面_NotificationElement
的onNotification
方法,这里调用的就是NotificationListener
里我们实现的onNotification
方法,不过它会先判断我们的范型是否一致,要是范型一致的话就调用,不一致的话就继续调用父组件的dispatchNotification
方法。
@override
bool onNotification(Notification notification) {
final NotificationListener<T> listener = widget as NotificationListener<T>;
if (listener.onNotification != null && notification is T) {
return listener.onNotification!(notification);
}
return false;
}
SizeChangedLayoutNotification
这是一个监听组件Size
改变的Notification
,比如我们想在父组件获取子组件的大小就可以用这个.
下面的代码是在官方的SizeChangedLayoutNotifier
加了一些修改.
class SizeChangedLayoutNotification extends Notification {
final Size size;
SizeChangedLayoutNotification(this.size);
}
class SizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
const SizeChangedLayoutNotifier({
Key key,
Widget child,
}) : super(key: key, child: child);
@override
_RenderSizeChangedWithCallback createRenderObject(BuildContext context) {
return _RenderSizeChangedWithCallback(
onLayoutChangedCallback: (Size size) {
SizeChangedLayoutNotification(size).dispatch(context);
}
);
}
}
class _RenderSizeChangedWithCallback extends RenderProxyBox {
_RenderSizeChangedWithCallback({
RenderBox child,
@required this.onLayoutChangedCallback,
}) : assert(onLayoutChangedCallback != null),
super(child);
final ValueChanged<Size> onLayoutChangedCallback;
Size _oldSize;
@override
void performLayout() {
super.performLayout();
if (size != _oldSize)
onLayoutChangedCallback(size);
_oldSize = size;
}
}
使用代码.这里只做演示,SizeChangedLayoutNotification
和官方的重名了,官方的widget
是只发通知,不会传递size
,如果想实际使用下面的代码,要先改一下组件的名称.
NotificationListener<SizeChangedLayoutNotification>(
onNotification: (SizeChangedLayoutNotification notification) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
setState(() {
childSize = notification.size;
});
});
return true;
},
child: SizeChangedLayoutNotifier(
child: child
),
)
每当子child
大小发生改变的时候我们就会收到SizeChangedLayoutNotification
的通知.
转载自:https://juejin.cn/post/6911233021320364039