FlutterDojo设计之道——状态管理之路(二)

书接上回,咱们讲到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返回值', [truefalse], [truefalse], (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源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索