Files
flutter_wisetronic/lib/utils/shop_scroll_position.dart
2021-08-31 13:28:33 -04:00

292 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:math' as math;
import 'package:flutter/gestures.dart';
import 'package:flutter/physics.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'shop_scroll_coordinator.dart';
/// 滑动位置信息
class ShopScrollPosition extends ScrollPosition
implements ScrollActivityDelegate {
final ShopScrollCoordinator coordinator; // 协调器
ScrollDragController _currentDrag;
double _heldPreviousVelocity = 0.0;
ShopScrollPosition(
{@required ScrollPhysics physics,
@required ScrollContext context,
double initialPixels = 0.0,
bool keepScrollOffset = true,
ScrollPosition oldPosition,
String debugLabel,
@required this.coordinator})
: super(
physics: physics,
context: context,
keepScrollOffset: keepScrollOffset,
oldPosition: oldPosition,
debugLabel: debugLabel,
) {
// 如果oldPosition不为null则父级将首先调用Absorb()它可以设置_pixels和_activity.
if (!hasPixels && initialPixels != null) correctPixels(initialPixels);
if (activity == null) goIdle();
assert(activity != null);
}
@override
AxisDirection get axisDirection => context.axisDirection;
@override
double setPixels(double newPixels) {
assert(activity.isScrolling);
return super.setPixels(newPixels);
}
@override
void absorb(ScrollPosition other) {
super.absorb(other);
if (other is! ScrollPositionWithSingleContext) {
goIdle();
return;
}
activity.updateDelegate(this);
final ShopScrollPosition typedOther = other as ShopScrollPosition;
_userScrollDirection = typedOther._userScrollDirection;
assert(_currentDrag == null);
if (typedOther._currentDrag != null) {
_currentDrag = typedOther._currentDrag;
_currentDrag.updateDelegate(this);
typedOther._currentDrag = null;
}
}
@override
void applyNewDimensions() {
super.applyNewDimensions();
context.setCanDrag(physics.shouldAcceptUserOffset(this));
}
/// 返回未使用的增量。
/// 正增量表示下降(在上方显示内容),负增量向上(在下方显示内容)。
double applyClampedDragUpdate(double delta) {
assert(delta != 0.0);
// 如果我们要朝向maxScrollExtent负滚动偏移那么我们在minScrollExtent方向上
// 可以达到的最大距离是负无穷大。 例如,如果我们已经过度滚动,则滚动以减少过度滚动不应禁止过度滚动。
// 如果我们要朝minScrollExtent正滚动偏移量方向移动那么我们在minScrollExtent方向
// 上可以达到的最大距离是我们现在所处的位置(如果我们已经过度滚动)
// 在这种情况下像素小于minScrollExtent或者如果minScrollExtent可以 我们不是。
// 换句话说我们不能通过applyClampedDragUpdate进入过滚动状态。
// 尽管如此,可能通过多种方式进入了过度滚动的情况。 一种是物理是否允许通过
// applyFullDragUpdate请参见下文。也可能会发生过度滚动的情况例如 如果使用滚动控制器人工设置了滚动位置。
final double min =
delta < 0.0 ? -double.infinity : math.min(minScrollExtent, pixels);
// max的逻辑是等效的但反向。
final double max =
delta > 0.0 ? double.infinity : math.max(maxScrollExtent, pixels);
final double oldPixels = pixels;
final double newPixels = (pixels - delta).clamp(min, max) as double;
final double clampedDelta = newPixels - pixels;
if (clampedDelta == 0.0) return delta;
final double overScroll = physics.applyBoundaryConditions(this, newPixels);
final double actualNewPixels = newPixels - overScroll;
final double offset = actualNewPixels - oldPixels;
if (offset != 0.0) {
forcePixels(actualNewPixels);
didUpdateScrollPositionBy(offset);
}
return delta + offset;
}
// Returns the overScroll. 返回过度滚动。
double applyFullDragUpdate(double delta) {
assert(delta != 0.0);
final double oldPixels = pixels;
// Apply friction: 施加摩擦:
final double newPixels =
pixels - physics.applyPhysicsToUserOffset(this, delta);
if (oldPixels == newPixels) return 0.0; // 增量一定很小,我们在添加浮点数时将其删除了
// Check for overScroll: 检查过度滚动:
final double overScroll = physics.applyBoundaryConditions(this, newPixels);
final double actualNewPixels = newPixels - overScroll;
if (actualNewPixels != oldPixels) {
forcePixels(actualNewPixels);
didUpdateScrollPositionBy(actualNewPixels - oldPixels);
}
return overScroll;
}
/// 当手指滑动时,该方法会获取到滑动距离
/// [delta]滑动距离,正增量表示下滑,负增量向上滑
/// 我们需要把子部件的 滑动数据 交给协调器处理,主部件无干扰
@override
void applyUserOffset(double delta) {
ScrollDirection userScrollDirection =
delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse;
if (debugLabel != coordinator.pageLabel)
return coordinator.applyUserOffset(delta, userScrollDirection, this);
updateUserScrollDirection(userScrollDirection);
setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
}
@override
void beginActivity(ScrollActivity newActivity) {
_heldPreviousVelocity = 0.0;
if (newActivity == null) return;
assert(newActivity.delegate == this);
super.beginActivity(newActivity);
_currentDrag?.dispose();
_currentDrag = null;
if (!activity.isScrolling) updateUserScrollDirection(ScrollDirection.idle);
}
/// 将[用户滚动方向]设置为给定值。
/// 如果更改了该值,则将分派[User ScrollNotification]。
@protected
@visibleForTesting
void updateUserScrollDirection(ScrollDirection value) {
assert(value != null);
if (userScrollDirection == value) return;
_userScrollDirection = value;
didUpdateScrollDirection(value);
}
@override
ScrollHoldController hold(VoidCallback holdCancelCallback) {
final double previousVelocity = activity.velocity;
final HoldScrollActivity holdActivity =
HoldScrollActivity(delegate: this, onHoldCanceled: holdCancelCallback);
beginActivity(holdActivity);
_heldPreviousVelocity = previousVelocity;
return holdActivity;
}
@override
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
final ScrollDragController drag = ScrollDragController(
delegate: this,
details: details,
onDragCanceled: dragCancelCallback,
carriedVelocity: physics.carriedMomentum(_heldPreviousVelocity),
motionStartDistanceThreshold: physics.dragStartDistanceMotionThreshold,
);
beginActivity(DragScrollActivity(this, drag));
assert(_currentDrag == null);
_currentDrag = drag;
return drag;
}
@override
void goIdle() {
beginActivity(IdleScrollActivity(this));
}
/// 以特定的速度开始一个物理驱动的模拟,该模拟确定[pixels]位置。
/// 此方法遵从[ScrollPhysics.createBallisticSimulation],该方法通常在当前位置超出
/// 范围时提供滑动模拟,而在当前位置超出范围但具有非零速度时提供摩擦模拟。
/// 速度应以每秒逻辑像素为单位。
@override
void goBallistic(double velocity, [bool fromCoordinator = false]) {
if (debugLabel != coordinator.pageLabel) {
if (velocity > 0.0) coordinator.goBallistic(velocity);
} else {
if (fromCoordinator && velocity <= 0.0) return;
if (coordinator.pageExpand == PageExpandState.Expanding) return;
}
assert(pixels != null);
final Simulation simulation =
physics.createBallisticSimulation(this, velocity);
if (simulation != null) {
beginActivity(BallisticScrollActivity(this, simulation, context.vsync));
} else {
goIdle();
}
}
@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent,
[bool fromCoordinator = false]) {
if (debugLabel == coordinator.pageLabel && !fromCoordinator)
return coordinator.applyContentDimensions(
minScrollExtent, maxScrollExtent, this);
return super.applyContentDimensions(minScrollExtent, maxScrollExtent);
}
@override
ScrollDirection get userScrollDirection => _userScrollDirection;
ScrollDirection _userScrollDirection = ScrollDirection.idle;
@override
Future<void> animateTo(
double to, {
@required Duration duration,
@required Curve curve,
}) {
if (nearEqual(to, pixels, physics.tolerance.distance)) {
// 跳过动画,直接移到我们已经靠近的位置。
jumpTo(to);
return Future<void>.value();
}
final DrivenScrollActivity activity = DrivenScrollActivity(
this,
from: pixels,
to: to,
duration: duration,
curve: curve,
vsync: context.vsync,
);
beginActivity(activity);
return activity.done;
}
@override
void jumpTo(double value) {
goIdle();
if (pixels != value) {
final double oldPixels = pixels;
forcePixels(value);
notifyListeners();
didStartScroll();
didUpdateScrollPositionBy(pixels - oldPixels);
didEndScroll();
}
goBallistic(0.0);
}
@Deprecated('This will lead to bugs.')
@override
void jumpToWithoutSettling(double value) {
goIdle();
if (pixels != value) {
final double oldPixels = pixels;
forcePixels(value);
notifyListeners();
didStartScroll();
didUpdateScrollPositionBy(pixels - oldPixels);
didEndScroll();
}
}
@override
void dispose() {
_currentDrag?.dispose();
_currentDrag = null;
super.dispose();
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('${context.runtimeType}');
description.add('$physics');
description.add('$activity');
description.add('$userScrollDirection');
}
@override
void pointerScroll(double delta) {
// TODO: implement pointerScroll
}
}