Flutter不使用setState实现CustomScrollView滚动时按需禁止滚动的方案
解决方案
方案1:使用ValueListenableBuilder局部刷新
该方案仅刷新CustomScrollView的physics属性,不会触发子Sliver组件重绘,对原有代码改动最小:
实现步骤
- 替换状态存储:将原来的布尔状态
canScroll改为ValueNotifier<bool>,无需绑定页面State - 移除setState调用:滚动监听中直接修改
ValueNotifier.value即可触发局部刷新 - 用
ValueListenableBuilder包裹CustomScrollView,仅更新physics属性,子Sliver树保持不变不会重绘
示例代码
// 定义可监听的滚动状态 final ValueNotifier<bool> _canScroll = ValueNotifier(true); @override Widget build(BuildContext context) { return NotificationListener( onNotification: (ScrollNotification notif) { if(notif is ScrollUpdateNotification) { if (_canScroll.value && notif.metrics.pixels > 100) { // 直接修改value即可,无需setState,仅触发ValueListenableBuilder局部刷新 _canScroll.value = false; } } if(notif is ScrollEndNotification) { if(!_canScroll.value) { _canScroll.value = true; } } return true; }, child: ValueListenableBuilder<bool>( valueListenable: _canScroll, builder: (context, canScroll, child) { return CustomScrollView( shrinkWrap: true, physics: canScroll ? const BouncingScrollPhysics() : const NeverScrollableScrollPhysics(), // 子sliver树直接复用,不会触发重绘 slivers: const [ SliverToBoxAdapter(), // 原有SliverList等组件直接放在这里即可,无需修改 ], ); }, ), ); }
方案2:自定义ScrollPhysics(性能最优)
无需任何状态管理和组件刷新操作,直接在滚动物理规则层面限制滚动阈值,全程无组件重绘:
实现步骤
- 自定义继承自ScrollPhysics的规则类,在边界判断方法中限制最大滚动偏移
- 直接将CustomScrollView的physics设置为自定义的物理规则即可
示例代码
// 自定义滚动物理规则 class LimitScrollPhysics extends ScrollPhysics { final double maxAllowedOffset; const LimitScrollPhysics({super.parent, required this.maxAllowedOffset}); @override LimitScrollPhysics applyTo(ScrollPhysics? ancestor) { return LimitScrollPhysics( parent: buildParent(ancestor), maxAllowedOffset: maxAllowedOffset, ); } @override double applyBoundaryConditions(ScrollMetrics position, double value) { // 滚动偏移超过阈值时阻止继续滚动 if (value > maxAllowedOffset) { return value - maxAllowedOffset; } return super.applyBoundaryConditions(position, value); } } // 使用时直接替换physics即可,无需NotificationListener和状态管理 CustomScrollView( shrinkWrap: true, physics: const LimitScrollPhysics(maxAllowedOffset: 100), slivers: const [ // 原有sliver组件保持不变 ], )
如果需要保留「滚动结束后恢复滚动能力」的逻辑,只需要在自定义Physics中结合ScrollController的监听,判断当前是否处于滚动结束状态调整阈值即可。
内容的提问来源于stack exchange,提问作者nicover
相关产品推荐
相关产品推荐

