handleObserveViewport method

SliverViewportObserveModel? handleObserveViewport({
  1. bool isForceObserve = false,
  2. bool isDependObserveCallback = true,
})

To observe the viewport.

Implementation

SliverViewportObserveModel? handleObserveViewport({
  bool isForceObserve = false,
  bool isDependObserveCallback = true,
}) {
  final isForbidObserveViewportCallback =
      widget.sliverController?.isForbidObserveViewportCallback ?? false;
  final onObserveViewport =
      isForbidObserveViewportCallback ? null : widget.onObserveViewport;
  if (isDependObserveCallback && onObserveViewport == null) return null;

  final isHandlingScroll =
      widget.sliverController?.innerIsHandlingScroll ?? false;
  if (isHandlingScroll) return null;

  final ctxs = fetchTargetSliverContexts();
  final objList = ctxs.map((e) => ObserverUtils.findRenderObject(e)).toList();
  if (objList.isEmpty) return null;
  final firstObj = objList.first;
  if (firstObj == null) return null;
  final viewport = ObserverUtils.findViewport(firstObj);
  if (viewport == null) return null;
  final viewportOffset = viewport.offset;
  if (viewportOffset is! ScrollPosition) return null;

  var targetChild = viewport.firstChild;
  if (targetChild == null) return null;
  var offset = widget.leadingOffset;
  if (widget.dynamicLeadingOffset != null) {
    offset = widget.dynamicLeadingOffset!();
  }
  final pixels = viewportOffset.pixels;
  final startCalcPixels = pixels + offset;

  int indexOfTargetChild = objList.indexOf(targetChild);

  // Find out the first sliver which is displayed in viewport.
  final dimension = viewportOffset.viewportDimension;
  final viewportBottomOffset = pixels + dimension;

  while (!ObserverUtils.isValidListIndex(indexOfTargetChild) ||
      !ObserverUtils.isDisplayingSliverInViewport(
        sliver: targetChild,
        viewportPixels: startCalcPixels,
        viewportBottomOffset: viewportBottomOffset,
      )) {
    if (targetChild == null) break;
    final nextChild = viewport.childAfter(targetChild);
    if (nextChild == null) break;
    targetChild = nextChild;
    indexOfTargetChild = objList.indexOf(targetChild);
  }

  if (targetChild == null ||
      !ObserverUtils.isValidListIndex(indexOfTargetChild)) return null;
  final targetCtx = ctxs[indexOfTargetChild];
  final firstChild = SliverViewportObserveDisplayingChildModel(
    sliverContext: targetCtx,
    sliver: targetChild,
  );

  List<SliverViewportObserveDisplayingChildModel> displayingChildModelList = [
    firstChild
  ];

  // Find the remaining children that are being displayed.
  targetChild = viewport.childAfter(targetChild);
  while (targetChild != null) {
    // The current targetChild is not displayed, so the later children don't
    // need to be check
    if (!ObserverUtils.isDisplayingSliverInViewport(
      sliver: targetChild,
      viewportPixels: startCalcPixels,
      viewportBottomOffset: viewportBottomOffset,
    )) break;

    indexOfTargetChild = objList.indexOf(targetChild);
    if (ObserverUtils.isValidListIndex(indexOfTargetChild)) {
      // The current targetChild is target.
      final context = ctxs[indexOfTargetChild];
      displayingChildModelList.add(SliverViewportObserveDisplayingChildModel(
        sliverContext: context,
        sliver: targetChild,
      ));
    }
    // continue to check next child.
    targetChild = viewport.childAfter(targetChild);
  }
  var model = SliverViewportObserveModel(
    viewport: viewport,
    firstChild: firstChild,
    displayingChildModelList: displayingChildModelList,
  );
  bool canReturnResult = false;
  if (isForceObserve ||
      widget.triggerOnObserveType == ObserverTriggerOnObserveType.directly) {
    canReturnResult = true;
  } else if (model != lastViewportObserveResultModel) {
    canReturnResult = true;
  }
  if (canReturnResult &&
      isDependObserveCallback &&
      onObserveViewport != null) {
    onObserveViewport(model);
  }

  // Record it for the next comparison.
  lastViewportObserveResultModel = model;

  return canReturnResult ? model : null;
}