这是我参与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 | |
在研究 ScrollView
源码时,有个很有意思的收获。这里作为引子,来引入 NotificationListener
组件。下面是 ScrollView#build
源码中的一部分,可以看出,当 keyboardDismissBehavior
为 onDrag
时,所构建的组件上层会嵌套一个 NotificationListener
组件,并在 onNotification
中进行逻辑性处理。
其中 ScrollViewKeyboardDismissBehavior
是只有两个元素的枚举。
dart enum ScrollViewKeyboardDismissBehavior { manual, onDrag, }
ListView
继承自ScrollView
,构造中的 keyboardDismissBehavior
参数,会为 ScrollView
中定义的该成员进行初始化。测试的核心代码如下:
manual
和 onDrag
的效果如下:当前 键盘弹出时
,如果为 manual
,列表滑动过程中键盘不会主动隐藏
。为 onDrag
时,滑动列表时,键盘会主动隐藏
。
| manual | onDrag | | ------------------------------------------------------------ | ------------------------------------------------------------ | | | |
通过源码中的一个小细节
处理,我们能够清楚地认识到 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
方法触发场合。
上面涉及到了很多处 Notification
,如果不了解这个类,那么很难对 NotificationListener
有全面的认识。Notification
是一个 抽象类
,它没有继承任何类。其中有两个普通方法 visitAncestor
和 dispatch
。
既然 Notification
是抽象类,那么并不能直接构造对象,所以 Flutter
框架中自然要提供相关的实现类,如下是 Notification
的众多实现类,包括引子中的 ScrollUpdateNotification
。这样我们就知道能监听到哪些 Notification
。
比如下面我们通过 NotificationListener
监听 ScrollUpdateNotification
,这样滑动时_onNotification
回调就可以回调出 notification
滑动数据。我们就可以根据这个对象进行相关逻辑处理。
```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
组件进行展示。
```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
。
dart class ListViewDemo extends StatelessWidget { const ListViewDemo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return NotificationListener
除了对某一种 Notification
监听,我们也可以通过父级来监听多个 Notification
,下面是监听顶层抽象 Notification
类的代码。简单上滑后日志如下,可以看出,这样能够同时监听到多种类型的 Notification
通知,我们可以通过类型判断来进行区分。
```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; } } ```
最经典的当属 Scrollbar
源码中对 NotificationListener
的使用,它监听 ScrollNotification
的五种通知,通过 _handleScrollNotification
进行处理。
这样只要在 ListView
外层嵌套一个 Scrollbar
,在滑动过程中右侧就可以出现指示器。
dart Scrollbar( child: ListView( children: List.generate( 60, (index) => ItemBox(index: index,)).toList()), ),
另外 RefreshIndicator
组件内部也是基于监听 ScrollNotification
和 OverscrollIndicatorNotification
通知进行实现的。
从源码中可以看出,当返回 false
则表示通知可以继续向上层节点分发。反之也就意味着通知被截断。
比如下面代码,将 NotificationListener
放在 Scrollbar
下方,监听时返回 true
。这样 ListView
的滑动事件向上分发时,到 NotificationListener
时,被拦截,就无法再向上传到 Scrollbar
中的监听。也就是说 Scrollbar
不起作用了。
Flutter
的滑动体系中通过 Notification
的分发与监听,让我们可以在任何地方去监听组件的滑动。这样滑动事件的得到了极大地解耦。至于滑动通知的具体流程,不是一言半语能够介绍完的。作为普通的使用者,了解到这样就已足够。我的第四本小册 《Flutter 滑动探索 - 珠联璧合》
中将会全面分析 Flutter
滑动体系的源码实现,敬请期待。