书接上回,咱们讲到Flutter中同Page下跨Widget的数据管理。android
第一种方案,咱们使用ValueNotifier和ValueListenableBuilder来实现了。git
此次,再介绍Flutter中的另外一种数据管理方式——Notification。严格来讲,Notification并非一个跨Widget数据管理方案,它只完成了一半的功能,即Notification实现了数据状态修改的通知,可是须要监听的Widget收到通知后的处理,仍是须要本身来实现的,这个实现,简单的说,能够是setState来重建UI,复杂了说,能够是配合其它任何一种数据管理/刷新的方案。github
Notification
Notification是Flutter中数据传递的一种机制。在Flutter的Widget树上,每一个节点均可以发出Notification,Notification会沿着当前节点向上传递,全部的父节点均可以经过NotificationListener来监听Notification的改动。web
Flutter中将这种由子节点向父节点传递Notification的机制称为通知冒泡(Notification Bubbling)。微信
Flutter中的不少地方使用了Notification,如Scrollable Widget在滑动时就会分发ScrollNotification,而Scrollbar正是经过监听ScrollNotification来肯定滚动条位置的。编辑器
除了ScrollNotification,Flutter中还有KeepAliveNotification、SizeChangedLayoutNotification、LayoutChangedNotification等不少子类。ide
那么Notification为何能够实现跨Widget的数据管理呢,首先,经过Notification机制有个使用条件,那就是父子关系,前面说了,父节点能够经过NotificationListener来监听子节点的Notification信息。因此借助Notification,能够很方便的从下至上传递数据的改变信息。函数
下面就经过一个系统的例子来演示下如何经过ScrollNotification,从滚动Widget拿到滚动数据。学习
class NotificationListenerWidget extends StatefulWidget {
@override
_NotificationListenerWidgetState createState() => _NotificationListenerWidgetState();
}
class _NotificationListenerWidgetState extends State<NotificationListenerWidget> {
final StreamController<String> _controller = StreamController();
var state = '';
@override
void dispose() {
_controller.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MainTitleWidget('NotificationListener基本使用'),
Expanded(
child: StreamBuilder(
initialData: '',
stream: _controller.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return Text(snapshot.data);
},
),
),
Expanded(
child: NotificationListener<ScrollNotification>(
child: ListView.builder(
itemBuilder: (BuildContext context, int position) {
return ListTile(
title: Text('ListTile:$position'),
);
},
itemCount: 30,
),
onNotification: (notification) {
switch (notification.runtimeType) {
case ScrollStartNotification:
state = '开始滚动';
break;
case ScrollUpdateNotification:
state = '正在滚动';
break;
case ScrollEndNotification:
state = '滚动中止';
break;
case OverscrollNotification:
state = '滚动到边界';
break;
}
_controller.add('depth:${notification.depth}\n'
'state:$state\n'
'metrics\n'
'-axisDirection:${notification.metrics.axisDirection}\n'
'-axis:${notification.metrics.axis}\n'
'-extentAfter:${notification.metrics.extentAfter}\n'
'-extentBefore:${notification.metrics.extentBefore}\n'
'-extentInside:${notification.metrics.extentInside}\n'
'-minScrollExtent:${notification.metrics.minScrollExtent}\n'
'-maxScrollExtent:${notification.metrics.maxScrollExtent}\n'
'-atEdge:${notification.metrics.atEdge}\n'
'-outOfRange:${notification.metrics.outOfRange}\n'
'-pixels:${notification.metrics.pixels}\n'
'-viewportDimension:${notification.metrics.viewportDimension}\n');
return false;
},
),
),
],
);
}
}
要获取滚动Widget的滚动数据,实际上就是在其父节点,经过NotificationListener来拿到其Notification改变的通知,NotificationListener能够指定接收Notification的具体类型,也能够在其内部经过runtimeType来进行判断,代码以下。flex
switch (notification.runtimeType){
case ScrollStartNotification: print("开始滚动"); break;
case ScrollUpdateNotification: print("正在滚动"); break;
case ScrollEndNotification: print("滚动中止"); break;
case OverscrollNotification: print("滚动到边界"); break;
}
不一样的通知类型,实际上封装了不一样的状态数据,

代码地址:Flutter Dojo-Widget-Scrolling-NotificationListener
Notification的可取消性
因为Notification是沿着父节点向上查找,因此Notification在传递到每一个父节点的时候,父节点均可以针对该Notification是否能够继续向上传递作出控制,源码以下所示。

因此,NotificationListener的onNotification回调是一个带bool返回值的函数,当返回false的时候,该Notification能够继续向上传递,不然则被该父节点拦截。
自定义Notification
Flutter封装了不少Notification,一样也支持自定义Notification,这也是使用Notification来进行数据管理的核心原理。
自定义Notification很是简单,只须要继承Notification便可,代码以下所示。
class MyNotification extends Notification {
MyNotification(this.msg);
final String msg;
}
接下来,就是本身实现通知的分发。
class NotificationListenerWidget extends StatefulWidget {
@override
_NotificationListenerWidgetState createState() => _NotificationListenerWidgetState();
}
class _NotificationListenerWidgetState extends State<NotificationListenerWidget> {
String msg = '';
String msgParent = '';
bool returnValue = true;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MainTitleWidget('自定义Notification'),
SubtitleWidget('onNotification返回值控制通知的传递'),
MultiSelectionWidget('onNotification返回值', [true, false], [true, false], (value) {
setState(() => returnValue = value);
}),
NotificationListener<MyNotification>(
onNotification: (notification) {
setState(() => msgParent = notification.msg);
return true;
},
child: NotificationListener<MyNotification>(
onNotification: (notification) {
setState(() => msg = notification.msg);
return returnValue;
},
child: Column(
children: [
Text('自定义Msg: $msg, 父Widget是否收到消息: ${msgParent.isEmpty ? false : true}'),
Builder(
builder: (BuildContext context) {
return RaisedButton(
onPressed: () {
msg = '';
msgParent = '';
MyNotification('MyNotification').dispatch(context);
},
child: Text('Send'),
);
},
),
],
),
),
),
],
);
}
}
代码地址:Flutter Dojo-Widget-Scrolling-NotificationListener
在使用自定义Notification的时候,须要注意几个地方。
-
继承Notification后,直接使用dispatch函数便可实现Notification的分发。 -
NotificationListener监听的是子节点,因此dispatch函数传入的context必须是子节点的Context,因此这里须要使用Builder来建立子节点的Context(建立新的Widget一样能够实现这个功能)。 -
要监听的Notification必定要是NotificationListener的child,缘由前面已经说了。

修仙
Flutter Dojo开源至今,受到了不少Flutter学习者和爱好者的喜好,也有愈来愈多的人加入到Flutter的学习中来,因此我建了个Flutter修仙群,可是人数太多,因此分红了【Flutter修仙指南】【Flutter修仙指北】【Flutter修仙指东】三个群,对Flutter感兴趣的朋友,能够留言。
项目地址:
https://github.com/xuyisheng/flutter_dojo
本文分享自微信公众号 - Android群英传(android_heroes)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。