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

相关推荐
|晴 天|34 分钟前
Vue 3 + TypeScript + Element Plus 博客系统开发总结与思考
前端·vue.js·typescript
猫3281 小时前
v-cloak
前端·javascript·vue.js
旷世奇才李先生1 小时前
Vue 3\+Vite\+Pinia实战:企业级前端项目架构设计
前端·javascript·vue.js
jiejiejiejie_2 小时前
Flutter 三方库 pull_to_refresh 的鸿蒙化适配指南
flutter·华为·harmonyos
SoaringHeart3 小时前
Flutter进阶:用OverlayEntry 实现所有弹窗效果
前端·flutter
IT_陈寒5 小时前
Vite静态资源加载把我坑惨了
前端·人工智能·后端
herinspace5 小时前
管家婆实用贴-如何分离和附加数据库
开发语言·前端·javascript·数据库·语音识别
小码哥_常5 小时前
从MVC到MVI:一文吃透架构模式进化史
前端
嗷o嗷o5 小时前
Android BLE 的 notify 和 indicate 到底有什么区别
前端
豹哥学前端5 小时前
别再背“var 提升,let/const 不提升”了:揭开暂时性死区的真实面目
前端·面试