Flutter ListView触摸触发scrollController.jumpTo后无法滚动,如何禁用ScrollPositionWithSingleContext的goIdle?
Flutter ListView触摸时调用jumpTo后滚动失效解决方案
触摸操作未结束(手指未离开屏幕)时调用
scrollController.jumpTo,原生ScrollPositionWithSingleContext的jumpTo方法会执行goIdle()重置滚动活动状态,打断当前正在进行的拖拽手势绑定,导致后续触摸事件无法驱动ListView滚动。
方案1:自定义ScrollPosition禁用拖拽状态下的goIdle逻辑(直接满足禁用goIdle的需求)
该方案直接修改jumpTo内部逻辑,仅在非用户拖拽场景保留原有goIdle、goBallistic逻辑,触摸拖拽过程中调用jumpTo不会触发状态重置,无业务侵入性。
class NoGoIdleJumpScrollPosition extends ScrollPositionWithSingleContext { NoGoIdleJumpScrollPosition({ required super.physics, required super.context, super.initialPixels, super.keepScrollOffset, super.oldPosition, super.debugLabel, }); @override void jumpTo(double value) { assert(activity!.isScrolling); // 仅非拖拽状态下执行goIdle,避免打断当前触摸手势 if (activity is! DragScrollActivity) { goIdle(); } if (pixels != value) { final double oldPixels = pixels; forcePixels(value); notifyListeners(); didUpdateScrollPositionBy(pixels - oldPixels); } // 仅非拖拽状态下执行惯性滚动逻辑 if (activity is! DragScrollActivity) { goBallistic(0.0); } } }
配套自定义ScrollController使用:
class NoGoIdleJumpScrollController extends ScrollController { NoGoIdleJumpScrollController({ super.initialScrollOffset, super.keepScrollOffset, super.debugLabel, }); @override ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) { return NoGoIdleJumpScrollPosition( physics: physics, context: context, initialPixels: initialScrollOffset, keepScrollOffset: keepScrollOffset, oldPosition: oldPosition, debugLabel: debugLabel, ); } }
使用方法:
- 将原有代码中使用的
ScrollController替换为自定义的NoGoIdleJumpScrollController即可,原有jumpTo调用逻辑无需修改。
方案2:调整jumpTo触发时机(无修改框架的轻量方案)
如果不想自定义滚动逻辑,可以调整跳转触发时机,避免在触摸未结束的回调中调用jumpTo:
- 不在
onTapDown、onPanUpdate这类触摸进行中的回调内执行jumpTo - 改在
onTapUp、onPanEnd等触摸结束的回调,或者通过WidgetsBinding.instance.addPostFrameCallback延迟到当前触摸事件处理完成后再执行跳转
注意事项
- 自定义ScrollPosition方案需要和你项目中使用的其他滚动自定义逻辑适配,避免出现滚动行为冲突
- 如果业务要求必须在拖拽过程中实时跳转,优先选择方案1,性能和体验优于延迟触发的方案2
内容的提问来源于stack exchange,提问作者surface丶rookie
相关产品推荐
相关产品推荐

