【Flutter 组件集录】NotificationListener| 8月更文挑战

前言:

这是我参与8月更文挑战的第 1 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

| 本系列 | 组件文章 | 列表 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1.NotificationListener | 2.Dismissible | 3.Switch | | 4.Scrollbar | 5.ClipPath | 6.CupertinoActivityIndicator | | 7.Opacity | 8.FadeTransition | 9. AnimatedOpacity | | 10. FadeInImage | 11. Offstage | 12. TickerMode | | 13. Visibility | 14. Padding | 15. AnimatedContainer | | 16.CircleAvatar | 17.PhysicalShape | 18.Divider | | 19.Flexible、Expanded 和 Spacer | 20.Card | |


1. 引子

在研究 ScrollView 源码时,有个很有意思的收获。这里作为引子,来引入 NotificationListener 组件。下面是 ScrollView#build 源码中的一部分,可以看出,当 keyboardDismissBehavioronDrag 时,所构建的组件上层会嵌套一个 NotificationListener 组件,并在 onNotification 中进行逻辑性处理。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第1张图片

其中 ScrollViewKeyboardDismissBehavior 是只有两个元素的枚举。

dart enum ScrollViewKeyboardDismissBehavior { manual, onDrag, }


ListView 继承自ScrollView ,构造中的 keyboardDismissBehavior 参数,会为 ScrollView 中定义的该成员进行初始化。测试的核心代码如下:

【Flutter 组件集录】NotificationListener| 8月更文挑战_第2张图片

manualonDrag 的效果如下:当前 键盘弹出时,如果为 manual ,列表滑动过程中键盘不会主动隐藏 。为 onDrag 时,滑动列表时,键盘会主动隐藏

| manual | onDrag | | ------------------------------------------------------------ | ------------------------------------------------------------ | | 【Flutter 组件集录】NotificationListener| 8月更文挑战_第3张图片 | 【Flutter 组件集录】NotificationListener| 8月更文挑战_第4张图片 |

通过源码中的一个小细节处理,我们能够清楚地认识到 NotificationListener 价值。它可以监听滑动的过程,回调出相关的数据让使用者进行逻辑处理


2. 认识 NotificationListener

首先 NotificationListener 是一个 StatelessWidget ,接受一个 Notification 族 泛型,构造方法中必须传入一个 child 组件,可以设置 onNotification 的监听。

onNotification 成员的类型为 NotificationListenerCallback ,可以看出它是一个函数类型,返回 bool 值。入参为 T 泛型对象,且必须是 Notification 子类 。也就是说,该函数会回调出一个数据,并且返回一个用于控制某个逻辑的标识。

```dart ---->[NotificationListener#onNotification 声明]---- final NotificationListenerCallback ? onNotification;

typedef NotificationListenerCallback = bool Function(T notification); ```


既然作为一个 StatelessWidget,那最重要的当属 build 方法。 但从源码中可以看出,该组件直接返回使用者传入的 child ,也就是说,它并不关心组件的构建逻辑。


最后,该类中还有一个私有方法 _dispatch ,该方法中需要传入 Notification 对象,可以看出,这里是使用者传入的 onNotification 方法触发场合。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第5张图片


3.认识 Notification

上面涉及到了很多处 Notification ,如果不了解这个类,那么很难对 NotificationListener 有全面的认识。Notification 是一个 抽象类 ,它没有继承任何类。其中有两个普通方法 visitAncestordispatch

【Flutter 组件集录】NotificationListener| 8月更文挑战_第6张图片


既然 Notification 是抽象类,那么并不能直接构造对象,所以 Flutter 框架中自然要提供相关的实现类,如下是 Notification 的众多实现类,包括引子中的 ScrollUpdateNotification 。这样我们就知道能监听到哪些 Notification

【Flutter 组件集录】NotificationListener| 8月更文挑战_第7张图片


4.认识 NotificationListener 的使用

比如下面我们通过 NotificationListener 监听 ScrollUpdateNotification ,这样滑动时_onNotification 回调就可以回调出 notification 滑动数据。我们就可以根据这个对象进行相关逻辑处理。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第8张图片

```dart class ListViewDemo extends StatelessWidget { const ListViewDemo({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return NotificationListener ( onNotification: _onNotification, child: ListView( children: List.generate( 60, (index) => ItemBox(index: index, )).toList()), ); }

bool _onNotification(ScrollUpdateNotification notification) { print('====dragDetails:${notification.dragDetails}' '====pixels:${notification.metrics.pixels}'); return false; } } ```


测试条目单体如下,使用 ItemBox 组件进行展示。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第9张图片

```dart class ItemBox extends StatelessWidget { final int index;

const ItemBox({Key? key, required this.index}) : super(key: key);

@override Widget build(BuildContext context) { return Container( alignment: Alignment.center, decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 1 / window.devicePixelRatio, ))), height: 56, child: Text( '第 $index 个', style: TextStyle(fontSize: 20), ), ); } } ```


我们可以监听任意的 Notification 类型,比如下面的 OverscrollNotification,这个监听将会在列表滑动到最顶端或最底端时被触发,在回调的数据中可以得到越界的尺寸 overscroll

【Flutter 组件集录】NotificationListener| 8月更文挑战_第10张图片

dart class ListViewDemo extends StatelessWidget { const ListViewDemo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return NotificationListener( onNotification: _onNotification, child: ListView( children: List.generate( 60, (index) => ItemBox( index: index,)).toList()), ); } bool _onNotification(OverscrollNotification notification) { print('====dragDetails:${notification.dragDetails}' '====pixels:${notification.metrics.pixels}' '=====overscroll:${notification.overscroll}'); return false; } }


除了对某一种 Notification 监听,我们也可以通过父级来监听多个 Notification,下面是监听顶层抽象 Notification 类的代码。简单上滑后日志如下,可以看出,这样能够同时监听到多种类型的 Notification 通知,我们可以通过类型判断来进行区分。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第11张图片

```dart class ListViewDemo extends StatelessWidget { const ListViewDemo({Key? key}) : super(key: key);

@override Widget build(BuildContext context) { return NotificationListener ( onNotification: _onNotification, child: ListView( children: List.generate( 60, (index) => ItemBox( index: index,)).toList()), ); }

bool _onNotification(Notification notification) { print('====Notification type:${notification.runtimeType}======'); return false; } } ```


5. 源码中对 NotificationListener 的使用

最经典的当属 Scrollbar 源码中对 NotificationListener 的使用,它监听 ScrollNotification 的五种通知,通过 _handleScrollNotification 进行处理。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第12张图片

这样只要在 ListView 外层嵌套一个 Scrollbar ,在滑动过程中右侧就可以出现指示器。

dart Scrollbar( child: ListView( children: List.generate( 60, (index) => ItemBox(index: index,)).toList()), ),

【Flutter 组件集录】NotificationListener| 8月更文挑战_第13张图片


另外 RefreshIndicator 组件内部也是基于监听 ScrollNotificationOverscrollIndicatorNotification 通知进行实现的。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第14张图片


6. NotificationListener 监听中返回值的作用

从源码中可以看出,当返回 false 则表示通知可以继续向上层节点分发。反之也就意味着通知被截断。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第15张图片

比如下面代码,将 NotificationListener 放在 Scrollbar 下方,监听时返回 true。这样 ListView 的滑动事件向上分发时,到 NotificationListener 时,被拦截,就无法再向上传到 Scrollbar 中的监听。也就是说 Scrollbar 不起作用了。

【Flutter 组件集录】NotificationListener| 8月更文挑战_第16张图片


Flutter 的滑动体系中通过 Notification 的分发与监听,让我们可以在任何地方去监听组件的滑动。这样滑动事件的得到了极大地解耦。至于滑动通知的具体流程,不是一言半语能够介绍完的。作为普通的使用者,了解到这样就已足够。我的第四本小册 《Flutter 滑动探索 - 珠联璧合》 中将会全面分析 Flutter 滑动体系的源码实现,敬请期待。

你可能感兴趣的:(flutter)