Flutter的滑动冲突 - 右滑父响应,左滑子响应
实现效果
右滑「父组件PageView」响应,左滑「子组件PageView的Item」响应

滑动基础组件
ScrollController - 滑动控制器
控制可滚动组件的滚动位置以及获取滚动状态属性(真正控制滚动行为的是ScrollPosition)
ScrollPhysics - 滑动物理模拟
滑动模拟器,将用户的交互处理成更接近真实世界的行为,例如,手指快速滑动之后,产生的惯性滑动效果。
- BouncingScrollPhysics : 允许滚动超出边界,但之后内容会反弹回来
- ClampingScrollPhysics : 防止滚动超出边界,夹住
- AlwaysScrollableScrollPhysics :默认,始终响应用户的滚动
- NeverScrollableScrollPhysics : 不响应用户的滚动
RawGestureDetector - 原始手势识别器
获取发生的所有「指针事件」,检测手势类型, 并将其发送到注册的识别器。
Drag - 滑动手势更新
当识别为滑动手势,控制器会调用「Drag.update」触发组件更新。同理,手势结束/取消,会调用「Drag.end/cancel」表示滑动结束。
代码实现
自定义手势识别器
默认情况下,子组件获得手势事件并消费事件,父组件是无法收到手势事件,因此自定义「RawGestureDetector」重写「rejectGesture」函数,在事件被拒绝时,手动处理为接收。
// 仅针对与横向滑动手势处理。
class CustomHorizontalDragGestureRecognizer
extends HorizontalDragGestureRecognizer {
@override
void rejectGesture(int pointer) {
acceptGesture(pointer);
}
}
滑动处理(滑动冲突核心)
- 滑动开始(可以理解为Down事件),默认父组件获取滑动事件。并获取父组件的「Drag」实例
- 滑动中,根据滑动方向来判断此滑动事件处理组件,设置Drag和ScrollPhysics滑动行为来控制
- 滑动结束(可以理解为Up事件)
// 初始化,定义滑动手势处理规则
initGestureRecognizer() {
_gestureRecognizers = <Type, GestureRecognizerFactory>{
CustomHorizontalDragGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
CustomHorizontalDragGestureRecognizer>(
() => CustomHorizontalDragGestureRecognizer(),
(CustomHorizontalDragGestureRecognizer instance) {
instance
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel;
}),
};
}
// 滑动开始
_handleDragStart(details) {
_drag = _pageController.position.drag(details, _disposeDrag);
}
// 滑动中
_handleDragUpdate(details) {
// 右滑pageview获取滑动事件
if ((details.delta.dx > 0 || details.delta.dx == 0) && !_isOpened) {
_refreshPhysics(false);
_drag?.update(details);
} else {
_refreshPhysics(true);
_drag?.cancel();
}
}
// 滑动未停止不处理滑动事件
_refreshPhysics(bool isSlideLeft) {
if (isSlideLeft) {
if (_physics.parent is BouncingScrollPhysics) {
_physics.applyTo(NeverScrollableScrollPhysics());
}
} else {
if (_physics.parent is NeverScrollableScrollPhysics) {
_physics.applyTo(BouncingScrollPhysics());
}
}
}
// 滑动结束
_handleDragEnd(details) {
_drag?.end(details);
}
_handleDragCancel() {
assert(_drag == null);
_drag?.cancel();
}
最终使用
RawGestureDetector(
gestures: _gestureRecognizers,
child: PageView.builder(
controller: _pageController,
itemCount: 5,
physics: _physics,
itemBuilder: (BuildContext context, int index) {
return itemWidget(index);
}),
);
源码地址
总结
此功能的实现,除了钻源码,还很感谢广大Flutter开发者的分享。
Flutter的生态相比于Android和iOS,还有很大差距。
不过,我相信参与分享的人会越来越多。
从我做起。我会把开发过程中遇到的问题和难点,尽可能通俗详细的分享。