Commit 28917728 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Refactor ScrollDragActivity into two pieces (#9629)

This will make it easier to implement nested scrolling.
parent 9ac2e44e
......@@ -20,9 +20,8 @@ import 'ticker_provider.dart';
abstract class ScrollActivityDelegate {
AxisDirection get axisDirection;
double get pixels;
double setPixels(double pixels);
double applyUserOffset(double delta);
void applyUserOffset(double delta);
void goIdle();
void goBallistic(double velocity);
......@@ -107,22 +106,29 @@ class IdleScrollActivity extends ScrollActivity {
bool get isScrolling => false;
}
class DragScrollActivity extends ScrollActivity implements Drag {
DragScrollActivity(
class ScrollDragController implements Drag {
ScrollDragController(
ScrollActivityDelegate delegate,
DragStartDetails details,
this.onDragCanceled,
) : _lastDetails = details, super(delegate);
) : _delegate = delegate, _lastDetails = details;
final VoidCallback onDragCanceled;
ScrollActivityDelegate get delegate => _delegate;
ScrollActivityDelegate _delegate;
@override
void didTouch() {
assert(false);
}
final VoidCallback onDragCanceled;
bool get _reversed => axisDirectionIsReversed(delegate.axisDirection);
/// Updates the controller's link to the [ScrollActivityDelegate].
///
/// This should only be called when a controller is being moved from a defunct
/// (or about-to-be defunct) [ScrollActivityDelegate] object to a new one.
void updateDelegate(ScrollActivityDelegate value) {
assert(_delegate != value);
_delegate = value;
}
@override
void update(DragUpdateDetails details) {
assert(details.primaryDelta != null);
......@@ -133,8 +139,6 @@ class DragScrollActivity extends ScrollActivity implements Drag {
if (_reversed) // e.g. an AxisDirection.up scrollable
offset = -offset;
delegate.applyUserOffset(offset);
// We ignore any reported overscroll returned by setPixels,
// because it gets reported via the reportOverscroll path.
}
@override
......@@ -155,41 +159,59 @@ class DragScrollActivity extends ScrollActivity implements Drag {
delegate.goBallistic(0.0);
}
@override
@mustCallSuper
void dispose() {
_lastDetails = null;
if (onDragCanceled != null)
onDragCanceled();
super.dispose();
}
dynamic get lastDetails => _lastDetails;
dynamic _lastDetails;
}
class DragScrollActivity extends ScrollActivity {
DragScrollActivity(
ScrollActivityDelegate delegate,
ScrollDragController controller,
) : _controller = controller, super(delegate);
ScrollDragController _controller;
@override
void didTouch() {
assert(false);
}
@override
void dispatchScrollStartNotification(ScrollMetrics metrics, BuildContext context) {
assert(_lastDetails is DragStartDetails);
new ScrollStartNotification(metrics: metrics, context: context, dragDetails: _lastDetails).dispatch(context);
final dynamic lastDetails = _controller.lastDetails;
assert(lastDetails is DragStartDetails);
new ScrollStartNotification(metrics: metrics, context: context, dragDetails: lastDetails).dispatch(context);
}
@override
void dispatchScrollUpdateNotification(ScrollMetrics metrics, BuildContext context, double scrollDelta) {
assert(_lastDetails is DragUpdateDetails);
new ScrollUpdateNotification(metrics: metrics, context: context, scrollDelta: scrollDelta, dragDetails: _lastDetails).dispatch(context);
final dynamic lastDetails = _controller.lastDetails;
assert(lastDetails is DragUpdateDetails);
new ScrollUpdateNotification(metrics: metrics, context: context, scrollDelta: scrollDelta, dragDetails: lastDetails).dispatch(context);
}
@override
void dispatchOverscrollNotification(ScrollMetrics metrics, BuildContext context, double overscroll) {
assert(_lastDetails is DragUpdateDetails);
new OverscrollNotification(metrics: metrics, context: context, overscroll: overscroll, dragDetails: _lastDetails).dispatch(context);
final dynamic lastDetails = _controller.lastDetails;
assert(lastDetails is DragUpdateDetails);
new OverscrollNotification(metrics: metrics, context: context, overscroll: overscroll, dragDetails: lastDetails).dispatch(context);
}
@override
void dispatchScrollEndNotification(ScrollMetrics metrics, BuildContext context) {
// We might not have DragEndDetails yet if we're being called from beginActivity.
final dynamic lastDetails = _controller.lastDetails;
new ScrollEndNotification(
metrics: metrics,
context: context,
dragDetails: _lastDetails is DragEndDetails ? _lastDetails : null
dragDetails: lastDetails is DragEndDetails ? lastDetails : null
).dispatch(context);
}
......@@ -198,6 +220,12 @@ class DragScrollActivity extends ScrollActivity implements Drag {
@override
bool get isScrolling => true;
@override
void dispose() {
_controller = null;
super.dispose();
}
}
class BallisticScrollActivity extends ScrollActivity {
......
......@@ -89,28 +89,28 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
assert(_pixels != null);
assert(SchedulerBinding.instance.schedulerPhase.index <= SchedulerPhase.transientCallbacks.index);
if (newPixels != pixels) {
final double overScroll = applyBoundaryConditions(newPixels);
final double overscroll = applyBoundaryConditions(newPixels);
assert(() {
final double delta = newPixels - pixels;
if (overScroll.abs() > delta.abs()) {
if (overscroll.abs() > delta.abs()) {
throw new FlutterError(
'$runtimeType.applyBoundaryConditions returned invalid overscroll value.\n'
'setPixels() was called to change the scroll offset from $pixels to $newPixels.\n'
'That is a delta of $delta units.\n'
'$runtimeType.applyBoundaryConditions reported an overscroll of $overScroll units.'
'$runtimeType.applyBoundaryConditions reported an overscroll of $overscroll units.'
);
}
return true;
});
final double oldPixels = _pixels;
_pixels = newPixels - overScroll;
_pixels = newPixels - overscroll;
if (_pixels != oldPixels) {
notifyListeners();
didUpdateScrollPositionBy(_pixels - oldPixels);
}
if (overScroll != 0.0) {
didOverscrollBy(overScroll);
return overScroll;
if (overscroll != 0.0) {
didOverscrollBy(overscroll);
return overscroll;
}
}
return 0.0;
......
......@@ -112,6 +112,14 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
assert(other.context == context);
super.absorb(other);
_userScrollDirection = other._userScrollDirection;
assert(_currentDrag == null);
if (other._currentDrag != null) {
other._currentDrag.updateDelegate(this);
_currentDrag = other._currentDrag;
other._currentDrag = null;
}
assert(activity == null);
assert(other.activity != null);
other.activity.updateDelegate(this);
......@@ -176,6 +184,8 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
oldIgnorePointer = false;
wasScrolling = false;
}
_currentDrag?.dispose();
_currentDrag = null;
_activity = newActivity;
isScrollingNotifier.value = activity.isScrolling;
if (!activity.isScrolling)
......@@ -187,9 +197,9 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
}
@override
double applyUserOffset(double delta) {
void applyUserOffset(double delta) {
updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse);
return setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
setPixels(pixels - physics.applyPhysicsToUserOffset(this, delta));
}
/// End the current [ScrollActivity], replacing it with an
......@@ -334,20 +344,27 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
activity.didTouch();
}
ScrollDragController _currentDrag;
/// Start a drag activity corresponding to the given [DragStartDetails].
///
/// The `dragCancelCallback` argument will be invoked if the drag is ended
/// The `onDragCanceled` argument will be invoked if the drag is ended
/// prematurely (e.g. from another activity taking over). See
/// [DragScrollActivity.onDragCanceled] for details.
/// [ScrollDragController.onDragCanceled] for details.
@override
DragScrollActivity drag(DragStartDetails details, VoidCallback dragCancelCallback) {
beginActivity(new DragScrollActivity(this, details, dragCancelCallback));
return activity;
Drag drag(DragStartDetails details, VoidCallback onDragCanceled) {
final ScrollDragController drag = new ScrollDragController(this, details, onDragCanceled);
beginActivity(new DragScrollActivity(this, drag));
assert(_currentDrag == null);
_currentDrag = drag;
return drag;
}
@override
void dispose() {
assert(pixels != null);
_currentDrag?.dispose();
_currentDrag = null;
activity?.dispose(); // it will be null if it got absorbed by another ScrollPosition
_activity = null;
super.dispose();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment