handleScroll method

bool handleScroll(
  1. ScrollNotification n
)

Implementation

bool handleScroll(ScrollNotification n) {
  if (canScroll != null && !canScroll!()) return false;
  // 只响应直接子滚动体的竖向滚动:页面内嵌套的横向/内层列表(depth > 0)
  // 不得驱动顶栏折叠,否则横滑一个内嵌列表也会牵动大标题。
  if (n.depth != 0 || n.metrics.axis != Axis.vertical) return false;

  final metrics = n.metrics;
  final pixels = metrics.pixels;
  final minExtent = metrics.minScrollExtent;
  final maxExtent = metrics.maxScrollExtent;

  // 短页面:最大可滚行程小于展开量,标题只能靠橡皮筋越界折叠,而越界
  // 松手必回弹——通用的位置同步会在回弹期间把标题重新拉回展开。走专用
  // 路径:回弹期冻结,松手后按阈值吸附到端点。
  final limit = state.heightOffsetLimit;
  final expansion = limit.isFinite ? -limit : 0.0;
  final canParkViaScroll =
      expansion <= 0 || (maxExtent - minExtent) >= expansion - 1.0;
  if (!canParkViaScroll) {
    return _handleShortPageScroll(n);
  }

  if (_snapInProgress && n is! ScrollEndNotification) {
    // 吸附动画运行期间仍按位置同步折叠量(保持 offset ≡ f(pixels))。
    _syncOffsetToPosition(metrics);
    return false;
  }

  // 松手时列表仍在底部越界:后续回弹动画期间冻结标题。
  if (n is ScrollEndNotification &&
      pixels > maxExtent + 0.5 &&
      state.heightOffset < 0) {
    _frozenDuringOverscrollSpringBack = true;
  }
  // 新手势开始 → 清除冻结与小标题锁。
  if (n is ScrollStartNotification) {
    _frozenDuringOverscrollSpringBack = false;
    _smallTitleLocked = false;
  }
  if (_frozenDuringOverscrollSpringBack) {
    state.contentOffset = pixels;
    if (pixels <= maxExtent + 0.5) {
      // 回弹结束;重新同步一次并清除标志。
      _frozenDuringOverscrollSpringBack = false;
      _syncOffsetToPosition(metrics);
    }
    return false;
  }

  if (_smallTitleLocked) {
    // 当前手势/惯性期间保持小标题冻结。
    state.contentOffset = pixels;
    return false;
  }

  _syncOffsetToPosition(metrics);

  // 可选增强:折叠后锁定小标题,直到新手势。
  if (lockSmallTitleUntilTop &&
      state.heightOffset < -5.0 &&
      pixels > 0.5) {
    _smallTitleLocked = true;
  }

  if (snapOnRelease && n is ScrollEndNotification) {
    _snapToNearestEndpoint(n);
  }
  return false;
}