Flutter 顶部滚动行为限制实现:NoTopOverScrollPhysics

一、需求来源

最近需要实现当列表页面嵌套在 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 的组件:

  • ListView
  • GridView
  • PageView
  • CustomScrollView
  • SingleChildScrollView

二、设置入口:

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%的开发场景。

github

相关推荐
英俊潇洒美少年3 分钟前
前端全量资源预加载优化指南(React内置API + Vue实现 + prerender/prefetch深度对比)
前端·react.js·前端框架
道友可好11 分钟前
3 个人,100 万行代码,一行都没人写:OpenAI 的 Harness Engineering 实验
前端·人工智能·后端
winfredzhang27 分钟前
用 Node.js + SQLite + 原生前端写一个本地情绪急救 Web App:情绪降落伞 Mood Parachute
前端·sqlite·node.js·express·情绪管理
樱花的浪漫1 小时前
Typescript、Zod基础
前端·javascript·人工智能·语言模型·自然语言处理·typescript
Bigger1 小时前
记一次坑爹的 Cloudflare Pages 部署:Failed to load module script 是怎么把我的 SPA 搞挂的
前端·ci/cd·浏览器
竹林8181 小时前
监听智能合约事件,我用 wagmi v2 踩了三天坑,终于找到了稳定方案
前端·javascript
星栈1 小时前
Makepad 界面怎么做得更像产品,而不是示例
前端·rust
Momo__1 小时前
SSR 懒水合四件套 — 99%的人不知道 Vue 3.5 藏了这些水合策略
前端·vue.js·性能优化
riuphan1 小时前
JavaScript 事件循环:单线程异步编程的核心机制
前端·javascript
YHHLAI1 小时前
告别传统开发!Bun + TS 解锁前端新体验
前端