createBallisticSimulation method

  1. @override
Simulation? createBallisticSimulation(
  1. ScrollMetrics position,
  2. double velocity
)
override

Returns a simulation for ballistic scrolling starting from the given position with the given velocity.

This is used by ScrollPositionWithSingleContext in the ScrollPositionWithSingleContext.goBallistic method. If the result is non-null, ScrollPositionWithSingleContext will begin a BallisticScrollActivity with the returned value. Otherwise, it will begin an idle activity instead.

The given position is only valid during this method call. Do not keep a reference to it to use later, as the values may update, may not update, or may update to reflect an entirely unrelated scrollable.

This method can potentially be called in every frame, even in the middle of what the user perceives as a single ballistic scroll. For example, in a ListView when previously off-screen items come into view and are laid out, this method may be called with a new ScrollMetrics.maxScrollExtent. The method implementation should ensure that when the same ballistic scroll motion is still intended, these calls have no side effects on the physics beyond continuing that motion.

Generally this is ensured by having the Simulation conform to a physical metaphor of a particle in ballistic flight, where the forces on the particle depend only on its position, velocity, and environment, and not on the current time or any internal state. This means that the time-derivative of Simulation.dx should be possible to write mathematically as a function purely of the values of Simulation.x, Simulation.dx, and the parameters used to construct the Simulation, independent of the time.

Implementation

@override
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
  // return super.createBallisticSimulation(position, velocity);
  final Tolerance tolerance = toleranceFor(position);
  const decelerationRate = ScrollDecelerationRate.normal;
  final loadTrigger = coordinator.loadTrigger;

  final headerIndicator = loadTrigger.headerIndicator;
  final footerIndicator = loadTrigger.footerIndicator;
  /// 最终决定在强制更新pixels的地方标记一下,不能再调用goBallstic了。
  if (coordinator.shouldRejectBallstic) {
    /// 恰好是触发惯性这时候
    if (coordinator.correctType == CorrectEnum.jmmpToItem) {
      return null;
    } else {
      /// 这种情况下一定是追加顶部内容,导致的,此刻一定是顶部loaded的时候
      /// TODO:这里可能是autoindicator或者下拉indicator
      if (footerIndicator != null) {
        WidgetsBinding.instance.addPostFrameCallback((d){
          // (position as MTScrollPositionWithSingleContext).activity?.resetActivity();
          (position as MTScrollPositionWithSingleContext).goBallistic(velocity);
        });
      }
    }
    mtLog("shouldRejectBallstic:${infiniteScorllController.coordinator.shouldRejectBallstic} ${FrameUtil.debugFrameCount}",
        tag: TagsConfig.tagShouldRejectBallistic);
    return null;
  }
  // if (parent != null) {
  //   return parent?.createBallisticSimulation(position, velocity);
  // }

  /// 如果当前是loading、loaded、状态,并且满足边界状态
  final pixels = position.pixels;
  double headProcessingExtent = 0.0;
  double footProcessingExtent = 0.0;

  bool headerNeedGoBallstic = false;
  bool footerNeedGoBallstic = false;
  double v = 330;
  if (headerIndicator != null) {
    headProcessingExtent = headerIndicator.needIndicatorHeight ? headerIndicator.indicatorHeight : 0;
    // if (headerIndicator.status == IndicatorStatusEnum.loaded) {
    /// MARK - 这里一定是由于新添加了内容导致尺寸发生变化,所以重新进入goBallstic,
    /// 我们保证loaded的时候一定是在这一帧的midMicro、build、直到postFrameCallback,
    /// 所以这里返回null,避免触发goBallstic,导致已经修正了pixel下一帧又被修改回去
    ///
    /// 为了避免出现拉动距离非常非常大,加载完新的内容,保持滚动后偏移仍然很大,出现空白的情况,
    /// 我们在状态改变为end之后进行惯性滚动,详细见[[MTIndicator.changeStatus]]
    //   return null;
    // }
    bool isProcessing = headerIndicator.physicProcessing;

    /// TODO:假设允许多个同时触发的话,那么这里就需要更多调整了!
    /// 需要考虑两个同时出现,单独出现一个的情况
    /// 必须在想等的时候返回null
    final minEdge = position.minScrollExtent - headProcessingExtent;
    if (isProcessing) {
      if (pixels < minEdge) {
        headerNeedGoBallstic = true;
      } else if (pixels == minEdge) {
        return null;
      } else if (pixels > minEdge && pixels < position.minScrollExtent) {
        if (headerIndicator.needShowFullIndicator) {
          double acceleration = 0;
          double distance = pixels;
          double endDistance = minEdge;
          double lvelocity = -v;
          final simu = ClampGravitySimulation(acceleration, distance, endDistance, lvelocity);
          return simu;
        } else {
          return null;
        }
      }
    }
    // overTriggerHeight = pixels < minEdge;
    // bool equalTriggerHeight = pixels == position.minScrollExtent - processingExtent;
    // if (isProcessing) {
    //   if (overTriggerHeight) {
    //     headerNeedGoBallstic = true;
    //   } else if (equalTriggerHeight) {
    //     return null;
    //   } else {
    //     ///TODO: 这里如果其他情况设置为false,则正在下拉加载的时候,划走,再快速划回来,很不容易置顶loading,更多的是回到正常边界位置
    //     ///并且设置成false也会出现同样得问题
    //     /// 如果设置成true,则会造成Ballistic无限进入Ballistic,
    //     /// 另外同样有问题,就是快速滑动超过边界,确实更容易整体将loading置顶,但是不超出边界的情况,并不会置顶loading或者回到正常边界,而是保持原位
    //     /// 设置成true,还有一个问题,就是滑动到idle状态下,超出的一部分
    //     // headerNeedGoBallstic = false;
    //     // headerNeedGoBallstic = true;
    //     double acceleration = 0.001;
    //     double distance = pixels;
    //     double endDistance = position.minScrollExtent - processingExtent;
    //     double lvelocity = velocity;
    //     final simu = ClampGravitySimulation(acceleration, distance, endDistance, lvelocity);
    //     return simu;
    //   }
    // }
  }

  if (footerIndicator != null) {
    footProcessingExtent = footerIndicator.needIndicatorHeight ? footerIndicator.indicatorHeight : 0;

    // if (footerIndicator.status == IndicatorStatusEnum.loaded) {
    //   /// MARK - 这里一定是由于新添加了内容导致尺寸发生变化,所以重新进入goBallstic,
    //   /// 我们保证loaded的时候一定是在这一帧的midMicro、build、直到postFrameCallback,
    //   /// 所以这里返回null,避免触发goBallstic,导致已经修正了pixel下一帧又被修改回去
    //   ///
    //   /// 为了避免出现拉动距离非常非常大,加载完新的内容,保持滚动后偏移仍然很大,出现空白的情况,
    //   /// 我们在状态改变为end之后进行惯性滚动,详细见[[MTIndicator.changeStatus]]
    //   return null;
    // }
    /// TODO:因为这里我们的status是在beginActivty之后修改的,所以松手的时候还可能是ready
    bool isProcessing = footerIndicator.physicProcessing;
    if (isProcessing) {
      final maxEdge = position.maxScrollExtent + footProcessingExtent;
      if (pixels > maxEdge) {
        headerNeedGoBallstic = true;
      } else if (pixels == maxEdge) {
        return null;
      } else if (pixels < maxEdge && pixels > position.maxScrollExtent) {
        if (footerIndicator.needShowFullIndicator) {
          double acceleration = 0;
          double distance = pixels;
          double endDistance = maxEdge;
          double lvelocity = v;
          final simu = ClampGravitySimulation(acceleration, distance, endDistance, lvelocity);
          return simu;
        } else {
          return null;
        }
      }
    }
  }

  /// TODO
  if (headerNeedGoBallstic || footerNeedGoBallstic) {
    double leadingExtent = position.minScrollExtent - headProcessingExtent;
    double trailingExtent = position.maxScrollExtent + footProcessingExtent;
    double constantDeceleration;
    switch (decelerationRate) {
      case ScrollDecelerationRate.fast:
        constantDeceleration = 1400;
      case ScrollDecelerationRate.normal:
        constantDeceleration = 0;
    }
    return BouncingScrollSimulation(
        spring: spring,
        position: position.pixels,
        velocity: velocity,
        leadingExtent: leadingExtent,
        trailingExtent: trailingExtent,
        tolerance: tolerance,
        constantDeceleration: constantDeceleration);
  }

  if (velocity.abs() >= tolerance.velocity || position.outOfRange) {
    double constantDeceleration;
    switch (decelerationRate) {
      case ScrollDecelerationRate.fast:
        constantDeceleration = 1400;
      case ScrollDecelerationRate.normal:
        constantDeceleration = 0;
    }
    return BouncingScrollSimulation(
        spring: spring,
        position: position.pixels,
        velocity: velocity,
        leadingExtent: position.minScrollExtent,
        trailingExtent: position.maxScrollExtent,
        tolerance: tolerance,
        constantDeceleration: constantDeceleration);
  }
  return null;
}