applyBoundaryConditions method

  1. @override
double applyBoundaryConditions(
  1. ScrollMetrics position,
  2. double value
)
override

应用边界条件到滚动位置

该方法用于控制滚动位置的边界条件,防止滚动超出允许的范围

参数:

  • position:当前滚动位置
  • value:期望的滚动位置
  • 返回值:需要调整的偏移量,如果不需要调整则返回0.0

Implementation

@override

/// 应用边界条件到滚动位置
///
/// 该方法用于控制滚动位置的边界条件,防止滚动超出允许的范围
///
/// 参数:
/// - [position]:当前滚动位置
/// - [value]:期望的滚动位置
/// - 返回值:需要调整的偏移量,如果不需要调整则返回0.0
double applyBoundaryConditions(ScrollMetrics position, double value) {
  final ScrollPosition scrollPosition = position as ScrollPosition;
  viewportRender ??= findViewport(controller!.position?.context.storageContext);

  // 检查内容是否不满一屏
  final bool isContentNotFull = position.minScrollExtent == position.maxScrollExtent;

  // 检查是否启用了下拉刷新和上拉加载
  final bool enablePullDown = viewportRender == null ? false : viewportRender!.firstChild is RenderSliverRefresh;
  final bool enablePullUp = viewportRender == null ? false : viewportRender!.lastChild is RenderSliverLoading;

  // 检查是否启用了对应方向的刷新
  if ((position.pixels - value > 0.0 && !enablePullDown) || (position.pixels - value < 0 && !enablePullUp)) {
    return parent!.applyBoundaryConditions(position, value);
  }

  // 计算顶部和底部的额外空间
  double topExtra = 0.0;
  double bottomExtra = 0.0;

  // 计算顶部额外空间(下拉刷新时)
  if (enablePullDown) {
    final RenderSliverRefresh sliverHeader = viewportRender!.firstChild as RenderSliverRefresh;
    topExtra = sliverHeader.hasLayoutExtent ? 0.0 : sliverHeader.refreshIndicatorLayoutExtent;
  }

  // 计算底部额外空间(上拉加载时)
  if (enablePullUp) {
    final RenderSliverLoading? sliverFooter = viewportRender!.lastChild as RenderSliverLoading?;
    bool shouldHideFooter = false;

    // 检查是否需要隐藏底部加载组件
    if (!isContentNotFull && sliverFooter!.geometry!.scrollExtent != 0) {
      shouldHideFooter = true;
    } else if (isContentNotFull) {
      // 内容不满一屏时的处理
      final refreshConfig = RefreshConfiguration.of(controller!.position!.context.storageContext);
      if (controller!.footerStatus == LoadStatus.noMore && !refreshConfig!.enableLoadingWhenNoData) {
        shouldHideFooter = true;
      } else if (refreshConfig?.hideFooterWhenNotFull ?? false) {
        shouldHideFooter = true;
      }
    }

    bottomExtra = shouldHideFooter ? 0.0 : (sliverFooter!.layoutExtent ?? 0.0);
  }

  // 计算最终的边界位置
  final double topBoundary = position.minScrollExtent - (maxOverScrollExtent ?? 0.0) - topExtra;
  final double bottomBoundary = position.maxScrollExtent + (maxUnderScrollExtent ?? 0.0) + bottomExtra;

  // 处理弹道滚动(惯性滚动)的边界条件
  if (scrollPosition.activity is BallisticScrollActivity) {
    // 处理顶部碰撞边界
    if (topHitBoundary != null && topHitBoundary != double.infinity) {
      if (value < -topHitBoundary! && -topHitBoundary! <= position.pixels) {
        // 碰到顶部边缘
        return value + topHitBoundary!;
      }
    }

    // 处理底部碰撞边界
    if (bottomHitBoundary != null && bottomHitBoundary != double.infinity) {
      if (position.pixels < bottomHitBoundary! + position.maxScrollExtent &&
          bottomHitBoundary! + position.maxScrollExtent < value) {
        // 碰到底部边缘
        return value - bottomHitBoundary! - position.maxScrollExtent;
      }
    }
  }

  // 处理超出顶部边界的情况
  if (maxOverScrollExtent != null &&
      maxOverScrollExtent != double.infinity &&
      value < topBoundary &&
      topBoundary < position.pixels) {
    // 碰到顶部边缘
    return value - topBoundary;
  }

  // 处理超出底部边界的情况
  if (maxUnderScrollExtent != null &&
      maxUnderScrollExtent != double.infinity &&
      position.pixels < bottomBoundary &&
      bottomBoundary < value) {
    // 碰到底部边缘
    return value - bottomBoundary;
  }

  // 处理用户拖动时的边界条件
  // 这很重要,因为不同设备在不同帧和时间可能有不同的弹跳行为,导致返回不同的速度
  if (scrollPosition.activity is DragScrollActivity) {
    // 处理上拉超出顶部边界的情况
    if (maxOverScrollExtent != null &&
        maxOverScrollExtent != double.infinity &&
        value < position.pixels &&
        position.pixels <= topBoundary) {
      // 上拉超出范围
      return value - position.pixels;
    }

    // 处理下拉超出底部边界的情况
    if (maxUnderScrollExtent != null &&
        maxUnderScrollExtent != double.infinity &&
        bottomBoundary <= position.pixels &&
        position.pixels < value) {
      // 下拉超出范围
      return value - position.pixels;
    }
  }

  // 不需要调整
  return 0.0;
}