一、吐槽现状
当看到这个标题时,大家首先想到的或许是 Flutter 手势冲突的解决
,随之而来的可能是晦涩难懂的介绍和源代码堆砌。但本文却另辟蹊径,摒弃了复杂的手势竞争管理处理方式,仅通过简洁的代码便可轻松解决滑动问题。接下来,就让我们一同领略这一神奇操作!
二、页面结构
1. 概括上面的图片表达的内容
一个页面包含一个 PageView1
, PageView1
又有两个视图PV1-View1
和 Pv1-View2
。 其中 PV1-View2
视图包含一个 PageView2
, 而这个 PageView2
也又有两个视图PV2-View1
和 Pv2-View2
。
2. 我们要解决的问题 (重点)
-
场景
当
PageView1
滑动到PV1-View2
时,然后PV1-View2
页面的PageView2
滑动到PV2-View1
时。 -
问题
在上面的场景下,当我们再次
向右
滑动PV2-View1
时, 页面没有自动切换到PageView1
的PV1-View1
视图。 -
期望结果
在滑动下能够流畅的切换到
PageView1
的PV1-View1
的视图, 然后,再向左
滑动进入PV1-View2
下PageView2
的Pv2-View1
的视图。 -
最终效果展示
三、解决问题的结论
实现步骤
-
我们使用
NotificationListener
包括PageView1
来监听PageView1
和PageView2
的滑动。 -
通过
NotificationListener 的 {bool Function(ScrollNotification)? onNotification}
方法获取超出滑动()的滑动信息OverscrollNotification
; -
在滑动信息
OverscrollNotification
中的depth
属性来区分是滑动的PageView1
还是PageView2
; -
然后由
OverscrollNotification
的overscroll
属性判断滑动的方向 (overscroll > 0
是向左滑动;overscroll < 0
是向右滑动); -
再由
OverscrollNotification
的velocity
属性判断是否向右
滑动到边缘。
注意事项
PageView
的 physics
属性在不同平台上默认值如下:
- Android:默认为
ClampingScrollPhysics
,这会阻止滚动超过内容范围。 - iOS:默认为
BouncingScrollPhysics
,允许滚动超过并回弹。
我们需要超出内容的滑动通知,而 IOS
的默认 physics
不满足需求,所以我们要修改 PageView2
的 physics
为 ClampingScrollPhysics
进行两端统一。
四、涉及知识点介绍
-
NotificationListener
在 Flutter 中,
NotificationListener
是一个非常强大的小部件,用于监听和处理滚动通知(如滚动开始、滚动结束、滚动位置变化等)。它通常与滚动组件(如ListView
、GridView
、SingleChildScrollView
或PageView
)结合使用,以监听滚动事件并根据需要执行操作。 类代码:class NotificationListener<T extends Notification> extends ProxyWidget { /// Creates a widget that listens for notifications. const NotificationListener({ super.key, required super.child, this.onNotification, }); final NotificationListenerCallback<T>? onNotification; @override Element createElement() { return _NotificationElement<T>(this); } }
NotificationListener
的使用非常简单,它需要一个onNotification
回调函数来处理通知。 -
OverscrollNotification
在 Flutter 中,
OverscrollNotification
是一种滚动通知,用于在滚动超出边界
时触发。类代码:class OverscrollNotification extends ScrollNotification { OverscrollNotification({ required super.metrics, required BuildContext super.context, this.dragDetails, required this.overscroll, this.velocity = 0.0, }) : assert(overscroll.isFinite), assert(overscroll != 0.0); // 如果 Scrollable 因拖动而过度滚动,则有关该拖动的详细信息将更新。 final DragUpdateDetails? dragDetails; // Scrollable 过度滚动的逻辑像素数。 final double overscroll; // 发生过度滚动时 ScrollPosition 变化的速度。 final double velocity; }
上面介绍了官方解释,下面我们用白话在絮叨一遍:
-
DragUpdateDetails? dragDetails
这个参数是当你过度的滑动时,当时的拖动信息, 默认值为
Null
。 就是说你不过度拖拽就不会有该属性值的更新。 -
double overscroll
这个属性就是你过度拖拽时 Scrollable 滚动的像素数。 这对于“开始”侧的过度滚动来说为负,而对于“结束”侧的过度滚动来说为正。
-
double velocity
这个就是你过度拖拽时 ScrollPosition 的变化速度。(
位置的变化,切记
) -
int depth
因为
OverscrollNotification
集成于ScrollNotification
,ScrollNotification
又混入ViewportNotificationMixin
, 而depth
是混入的参数属性。该属性是通知已冒泡的视口数量,通常监听器仅响应depth
为零的通知。大白话就是:用于区分你拖动的那个PageView
。
-
-
ClampingScrollPhysics
在 Flutter 中,
ClampingScrollPhysics
是一种滚动物理模型,用于限制滚动范围,防止滚动超出内容的实际范围。它通常用于PageView
和其他滚动组件,确保滚动只能在内容的范围内进行。
五、代码展示
/// PageView1 视图
NotificationListener<OverscrollNotification>(
onNotification: (notification) {
if (notification.depth == 1 && notification.overscroll < 0 && notification.velocity == 0) {
_pageController.animateToPage(0, duration: Duration(milliseconds: 200), curve: Curves.linear);
return false;
}
return true;
},
child: PageView.builder(
controller: _pageController,
... // 省略无用代码
).expanded(),
),
/// PageView2 的视图
PageView.builder(
physics: const ClampingScrollPhysics(),
...// 省略无用代码
)
六、总结
通过以上操作,我们就能实现 PageView
嵌套,流畅的切换了。小小的操作解决大大的问题,请为之打 Call 吧!!!