一、需求来源
最近需要实现当列表页面嵌套在 showModalBottomSheet 容器里,禁用下拉越界的需求(因为列表会和 showModalBottomSheet)一起向下滚动,极其诡异。就需要列表顶部禁用滚动,上拉加载更多仍在的行为。
二、使用示例
dart
physics: const NoTopOverScrollPhysics(),
三、源码
dart
//
// NoTopOverScrollPhysics.dart
// flutter_templet_project
//
// Created by shang on 2026/1/16 12:17.
// Copyright © 2026/1/16 shang. All rights reserved.
//
import 'package:flutter/material.dart';
class NoTopOverScrollPhysics extends ScrollPhysics {
const NoTopOverScrollPhysics({super.parent});
@override
NoTopOverScrollPhysics applyTo(ScrollPhysics? ancestor) {
return NoTopOverScrollPhysics(parent: buildParent(ancestor));
}
/// ① 用户拖动阶段(最关键)
@override
double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
// 在顶部 && 继续向下拖
if (position.pixels <= position.minScrollExtent && offset > 0) {
return offset; // 不消费,让手势传给上层
}
return super.applyPhysicsToUserOffset(position, offset);
}
/// ② 防止惯性或 ballistic 把位置拉到负值
@override
double applyBoundaryConditions(ScrollMetrics position, double value) {
// 顶部越界
if (value < position.minScrollExtent) {
return value - position.minScrollExtent;
}
return super.applyBoundaryConditions(position, value);
}
/// ③ 明确声明:允许用户滚动(否则 BottomSheet 会抢)
@override
bool shouldAcceptUserOffset(ScrollMetrics position) => true;
}
四、ScrollPhysics知识总结

一、ScrollPhysics 决定滚动视图"怎么滚",包括:
diff
- 能不能滚
- 怎么回弹
- 惯性有多大
- 边界怎么处理
它是 ScrollView 的"物理引擎"
二、ScrollPhysics 在哪生效?
所有使用 Scrollable 的组件:
ListViewGridViewPageViewCustomScrollViewSingleChildScrollView
二、设置入口:
dart
physics: const BouncingScrollPhysics(),
三、核心设计:Physics 链(极其重要)
less
BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
)
设计思想
- 责任链模式
- 子 physics 不处理 → 交给 parent
内部调用顺序(简化)
sql
User Drag
↓
applyPhysicsToUserOffset
↓
applyBoundaryConditions
↓
createBallisticSimulation
四、最常见内置 ScrollPhysics(必须掌握)
1️⃣ ClampingScrollPhysics(Android 默认)
- 到边界直接"卡住"
- 无回弹
csharp
ListView(physics: const ClampingScrollPhysics());
2️⃣ BouncingScrollPhysics(iOS 默认)
- 可超出边界
- 松手回弹
csharp
ListView(physics: const BouncingScrollPhysics());
3️⃣ NeverScrollableScrollPhysics
- 完全禁止滚动
csharp
ListView(physics: const NeverScrollableScrollPhysics());
4️⃣ AlwaysScrollableScrollPhysics
- 即使内容不足一屏也能滚
- 下拉刷新必备
5️⃣ PageScrollPhysics
- 专用于
PageView - 强制一页一页吸附
五、几个关键方法(理解这些 = 会自定义)
① applyPhysicsToUserOffset
手指拖动时的位移修正
arduino
@override
double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
return offset * 0.5; // 阻尼
}
② applyBoundaryConditions
是否允许越界
arduino
@override
double applyBoundaryConditions(ScrollMetrics position, double value) {
if (value < position.minScrollExtent) {
return value - position.minScrollExtent;
}
return 0;
}
返回值:
0→ 允许≠0→ 拒绝(消耗掉)
③ createBallisticSimulation
松手后的惯性 / 回弹
arduino
@override
Simulation? createBallisticSimulation(
ScrollMetrics position,
double velocity,
) {
if (velocity.abs() < tolerance.velocity) return null;
return ClampingScrollSimulation(...);
}
最后、总结
ScrollPhysics 是滚动知识体系的一部分,已经属于比较复杂的内容。各种滚动式行为自定义需要大量的实践,去细细体会不同设置的区别。现官方提供的几种基本已经能覆盖90%的开发场景。