前面提到InheritedWidget,它可以实现数据的冲上往下传递。那么从下往上传递呢?flutter 提供了 Notification 实现向上传递。
Notification中文意思是通知,与Android中的广播机制类似,在Flutter中Notification的功能是子节点状态变更,发送通知上报。
Notification的数据变更是通过Widgte树向上冒泡的,我们往往在下层Wdiget发送通知然后在上层处理通知。
//创建通知类
class MyNotification extends Notification {
final String info;
MyNotification(this.info);
}
//创建子Widget发送通知
class ChildWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
onPressed: () => MyNotification("我是随机下面发送的数据:${Random().nextInt(1000)}").dispatch(context),
child: Text("提交"),
),
);
}
}
//parentWidgte使用NotificationListener接收通知并显示
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
String msg = "";
onReceiveMessage(String message) {
setState(() {
msg =message;
});
}
@override
Widget build(BuildContext context) {
return NotificationListener<MyNotification>(
child: Column(
children: [Text(msg), ChildWidget()],
),
onNotification: (notification) {
onReceiveMessage(notification.info);
return true;
},
);
}
}
源码
可以看到主要是两个类,。
- dispatch()
- visitAncestor(),
按照使用流程来分析,自定义一个Notification,调用dispatch(context)方法 来传递消息。dispatch(context)中调用了当前context的visitAncestorElements方法,该方法会从当前Element开始向上遍历父级元素;visitAncestorElements有一个遍历回调参数,在遍历过程中对遍历到的父级元素都会执行该回调。遍历的终止条件是:已经遍历到根Element或某个遍历回调返回false。源码中传给visitAncestorElements方法的遍历回调为visitAncestor方法,
//遍历回调,会对每一个父级Element执行此回调
bool visitAncestor(Element element) {
//判断当前element对应的Widget是否是NotificationListener。
//由于NotificationListener是继承自StatelessWidget,
//故先判断是否是StatelessElement
if (element is StatelessElement) {
//是StatelessElement,则获取element对应的Widget,判断
//是否是NotificationListener 。
final StatelessWidget widget = element.widget;
if (widget is NotificationListener<Notification>) {
//是NotificationListener,则调用该NotificationListener的_dispatch方法
if (widget._dispatch(this, element))
return false;
}
}
return true;
}
visitAncestor会判断每一个遍历到的父级Widget是否是NotificationListener,如果不是,则返回true继续向上遍历,如果是,则调用NotificationListener的_dispatch方法,我们看看_dispatch方法的源码
bool _dispatch(Notification notification, Element element) {
// 如果通知监听器不为空,并且当前通知类型是该NotificationListener
// 监听的通知类型,则调用当前NotificationListener的onNotification
if (onNotification != null && notification is T) {
final bool result = onNotification(notification);
// 返回值决定是否继续向上遍历
return result == true;
}
return false;
}
我们可以看到NotificationListener的onNotification回调最终是在_dispatch方法中执行的,然后会根据返回值来确定是否继续向上冒泡。
1.Context上也提供了遍历Element树的方法。
2.我们可以通过Element.widget得到element节点对应的widget;我们已经反复讲过Widget和Element的对应关系,读者通过这些源码来加深理解。