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

Document more scrolling classes (#9684)

parent a4ba761b
......@@ -1261,6 +1261,10 @@ abstract class RenderSliverSingleBoxAdapter extends RenderSliver with RenderObje
child.parentData = new SliverPhysicalParentData();
}
/// Sets the [SliverPhysicalParentData.paintOffset] for the given child
/// according to the [SliverConstraints.axisDirection] and
/// [SliverConstraints.growthDirection] and the given geometry.
@protected
void setChildParentData(RenderObject child, SliverConstraints constraints, SliverGeometry geometry) {
final SliverPhysicalParentData childParentData = child.parentData;
assert(constraints.axisDirection != null);
......
......@@ -24,7 +24,8 @@ import 'scroll_view.dart';
import 'sliver.dart';
import 'ticker_provider.dart';
typedef List<Widget> NestedScrollViewOuterSliversBuilder(BuildContext context, bool innerBoxIsScrolled);
/// Signature used by [NestedScrollView] for building its header.
typedef List<Widget> NestedScrollViewHeaderSliversBuilder(BuildContext context, bool innerBoxIsScrolled);
class NestedScrollView extends StatefulWidget {
NestedScrollView({
......@@ -49,7 +50,7 @@ class NestedScrollView extends StatefulWidget {
final ScrollPhysics physics;
final NestedScrollViewOuterSliversBuilder headerSliverBuilder;
final NestedScrollViewHeaderSliversBuilder headerSliverBuilder;
final Widget body;
......@@ -415,9 +416,9 @@ class _NestedScrollCoorindator implements ScrollActivityDelegate {
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
final ScrollDragController drag = new ScrollDragController(
this,
details,
dragCancelCallback,
delegate: this,
details: details,
onDragCanceled: dragCancelCallback,
);
beginActivity(
new DragScrollActivity(_outerPosition, drag),
......
......@@ -7,7 +7,17 @@ import 'package:flutter/foundation.dart';
import 'framework.dart';
import 'scroll_controller.dart';
/// Associates a [ScrollController] with a subtree.
///
/// When a [ScrollView] has [ScrollView.primary] set to true and is not given
/// an explicit [ScrollController], the [ScrollView] uses [of] to find the
/// [ScrollController] associated with its subtree.
///
/// This mechanism can be used to provide default behavior for scroll views in a
/// subtree. For example, the [Scaffold] uses this mechanism to implement the
/// scroll-to-top gesture on iOS.
class PrimaryScrollController extends InheritedWidget {
/// Creates a widget that associates a [ScrollController] with a subtree.
const PrimaryScrollController({
Key key,
@required this.controller,
......@@ -15,14 +25,21 @@ class PrimaryScrollController extends InheritedWidget {
}) : assert(controller != null),
super(key: key, child: child);
/// Creates a subtree without an associated [ScrollController].
const PrimaryScrollController.none({
Key key,
@required Widget child
}) : controller = null,
super(key: key, child: child);
/// The [ScrollController] associated with the subtree.
final ScrollController controller;
/// Returns the [ScrollController] most closely associated with the given
/// context.
///
/// Returns null if there is no [ScrollController] associated with the given
/// context.
static ScrollController of(BuildContext context) {
final PrimaryScrollController result = context.inheritFromWidgetOfExactType(PrimaryScrollController);
return result?.controller;
......
......@@ -17,13 +17,37 @@ import 'scroll_metrics.dart';
import 'scroll_notification.dart';
import 'ticker_provider.dart';
/// A backend for a [ScrollActivity].
///
/// Used by subclases of [ScrollActivity] to manipulate the scroll view that
/// they are acting upon.
///
/// See also:
///
/// * [ScrollActivity], which uses this class as its delegate.
abstract class ScrollActivityDelegate {
/// The direction in which the scroll view scrolls.
AxisDirection get axisDirection;
/// Update the scroll position to the given pixel value.
///
/// Returns the overscroll, if any. See [ScrollPosition.setPixels] for more
/// information.
double setPixels(double pixels);
/// Updates the scroll position by the given amount.
///
/// Appropriate for when the user is directly manipulating the scroll
/// position, for example by dragging the scroll view. Typically applies
/// [ScrollPhysics.applyPhysicsToUserOffset] and other transformations that
/// are appropriate for user-driving scrolling.
void applyUserOffset(double delta);
/// Terminate the current activity and start an idle activity.
void goIdle();
/// Terminate the current activity and start a ballistic activity with the
/// given velocity.
void goBallistic(double velocity);
}
......@@ -31,11 +55,13 @@ abstract class ScrollActivityDelegate {
///
/// See also:
///
/// * [ScrollPositionWithSingleContext], which uses [ScrollActivity] objects to
/// manage the [ScrollPosition] of a [Scrollable].
/// * [ScrollPosition], which uses [ScrollActivity] objects to manage the
/// [ScrollPosition] of a [Scrollable].
abstract class ScrollActivity {
/// Initializes [delegate] for subclasses.
ScrollActivity(this._delegate);
/// The delegate that this activity will use to actuate the scroll view.
ScrollActivityDelegate get delegate => _delegate;
ScrollActivityDelegate _delegate;
......@@ -58,30 +84,43 @@ abstract class ScrollActivity {
/// [ScrollActivityDelegate.goBallistic].
void resetActivity() { }
/// Dispatch a [ScrollStartNotification] with the given metrics.
void dispatchScrollStartNotification(ScrollMetrics metrics, BuildContext context) {
new ScrollStartNotification(metrics: metrics, context: context).dispatch(context);
}
/// Dispatch a [ScrollUpdateNotification] with the given metrics and scroll delta.
void dispatchScrollUpdateNotification(ScrollMetrics metrics, BuildContext context, double scrollDelta) {
new ScrollUpdateNotification(metrics: metrics, context: context, scrollDelta: scrollDelta).dispatch(context);
}
/// Dispatch an [OverscrollNotification] with the given metrics and overscroll.
void dispatchOverscrollNotification(ScrollMetrics metrics, BuildContext context, double overscroll) {
new OverscrollNotification(metrics: metrics, context: context, overscroll: overscroll).dispatch(context);
}
/// Dispatch a [ScrollEndNotification] with the given metrics and overscroll.
void dispatchScrollEndNotification(ScrollMetrics metrics, BuildContext context) {
new ScrollEndNotification(metrics: metrics, context: context).dispatch(context);
}
/// Called when the user touches the scroll view that is performing this activity.
void didTouch() { }
/// Called when the scroll view that is performing this activity changes its metrics.
void applyNewDimensions() { }
/// Whether the scroll view should ignore pointer events while performing this
/// activity.
bool get shouldIgnorePointer;
/// Whether performing this activity constitutes scrolling.
///
/// Used, for example, to determine whether the user scroll direction is
/// [ScrollDirection.idle].
bool get isScrolling;
/// Called when the scroll view stops performing this activity.
@mustCallSuper
void dispose() {
_delegate = null;
......@@ -91,7 +130,11 @@ abstract class ScrollActivity {
String toString() => '$runtimeType';
}
/// A scroll activity that does nothing.
///
/// When a scroll view is not scrolling, it is performing the idle activity.
class IdleScrollActivity extends ScrollActivity {
/// Creates a scroll activity that does nothing.
IdleScrollActivity(ScrollActivityDelegate delegate) : super(delegate);
@override
......@@ -106,16 +149,31 @@ class IdleScrollActivity extends ScrollActivity {
bool get isScrolling => false;
}
/// Scrolls a scroll view as the user drags their finger across the screen.
///
/// See also:
///
/// * [DragScrollActivity], which is the activity the scroll view performs
/// while a drag is underway.
class ScrollDragController implements Drag {
ScrollDragController(
ScrollActivityDelegate delegate,
DragStartDetails details,
/// Creates an object that scrolls a scroll view as the user drags their
/// finger across the screen.
///
/// The [delegate] and `details` arguments must not be null.
ScrollDragController({
@required ScrollActivityDelegate delegate,
@required DragStartDetails details,
this.onDragCanceled,
) : _delegate = delegate, _lastDetails = details;
}) : _delegate = delegate, _lastDetails = details {
assert(delegate != null);
assert(details != null);
}
/// The object that will actuate the scroll view as the user drags.
ScrollActivityDelegate get delegate => _delegate;
ScrollActivityDelegate _delegate;
/// Called when [dispose] is called.
final VoidCallback onDragCanceled;
bool get _reversed => axisDirectionIsReversed(delegate.axisDirection);
......@@ -159,6 +217,7 @@ class ScrollDragController implements Drag {
delegate.goBallistic(0.0);
}
/// Called when the delegate is no longer sending events to this object.
@mustCallSuper
void dispose() {
_lastDetails = null;
......@@ -166,11 +225,22 @@ class ScrollDragController implements Drag {
onDragCanceled();
}
/// The most recently observed [DragStartDetails], [DragUpdateDetails], or
/// [DragEndDetails] object.
dynamic get lastDetails => _lastDetails;
dynamic _lastDetails;
}
/// The activity a scroll view performs when a the user drags their finger
/// across the screen.
///
/// See also:
///
/// * [ScrollDragController], which listens to the [Drag] and actually scrolls
/// the scroll view.
class DragScrollActivity extends ScrollActivity {
/// Creates an activity for when the user drags their finger across the
/// screen.
DragScrollActivity(
ScrollActivityDelegate delegate,
ScrollDragController controller,
......@@ -228,9 +298,23 @@ class DragScrollActivity extends ScrollActivity {
}
}
/// An activity that animates a scroll view based on a physics [simulation].
///
/// A [BallisticScrollActivity] is typically used when the user lifts their
/// finger off the screen to continue the scrolling gesture with the current velocity.
///
/// [BallisticScrollActivity] is also used to restore a scroll view to a valid
/// scroll offset when the geometry of the scroll view changes. In these
/// situations, the [simulation] typically starts with a zero velocity.
///
/// See also:
///
/// * [DrivenScrollActivity], which animates a scroll view based on a set of
/// animation parameters.
class BallisticScrollActivity extends ScrollActivity {
// ///
// /// The velocity should be in logical pixels per second.
/// Creates an activity that animates a scroll view based on a [simulation].
///
/// The [delegate], [simulation], and [vsync] arguments must not be null.
BallisticScrollActivity(
ScrollActivityDelegate delegate,
Simulation simulation,
......@@ -245,6 +329,8 @@ class BallisticScrollActivity extends ScrollActivity {
.whenComplete(_end); // won't trigger if we dispose _controller first
}
/// The velocity at which the scroll offset is currently changing (in logical
/// pixels per second).
double get velocity => _controller.velocity;
AnimationController _controller;
......@@ -271,8 +357,8 @@ class BallisticScrollActivity extends ScrollActivity {
/// Move the position to the given location.
///
/// If the new position was fully applied, return true.
/// If there was any overflow, return false.
/// If the new position was fully applied, returns true. If there was any
/// overflow, returns false.
///
/// The default implementation calls [ScrollActivityDelegate.setPixels]
/// and returns true if the overflow was zero.
......@@ -308,7 +394,20 @@ class BallisticScrollActivity extends ScrollActivity {
}
}
/// An activity that animates a scroll view based on animation parameters.
///
/// For example, a [DrivenScrollActivity] is used to implement
/// [ScrollController.animateTo].
///
/// See also:
///
/// * [BallisticScrollActivity], which animates a scroll view based on a
/// physics [Simulation].
class DrivenScrollActivity extends ScrollActivity {
/// Creates an activity that animates a scroll view based on animation
/// parameters.
///
/// All of the parameters must be non-null.
DrivenScrollActivity(
ScrollActivityDelegate delegate, {
@required double from,
......@@ -336,8 +435,15 @@ class DrivenScrollActivity extends ScrollActivity {
Completer<Null> _completer;
AnimationController _controller;
/// A [Future] that completes when the activity stops.
///
/// For example, this [Future] will complete if the animation reaches the end
/// or if the user interacts with the scroll view in way that causes the
/// animation to stop before it reaches the end.
Future<Null> get done => _completer.future;
/// The velocity at which the scroll offset is currently changing (in logical
/// pixels per second).
double get velocity => _controller.velocity;
@override
......
......@@ -8,11 +8,44 @@ import 'package:flutter/rendering.dart';
import 'framework.dart';
import 'ticker_provider.dart';
/// An interface that [Scrollable] widgets implement in order to use
/// [ScrollPosition].
///
/// See also:
///
/// * [ScrollableState], which is the most common implementation of this
/// interface.
/// * [ScrollPosition], which uses this interface to communicate with the
/// scrollable widget.
abstract class ScrollContext {
/// The [BuildContext] that should be used when dispatching
/// [ScrollNotification]s.
///
/// This context is typically different that the context of the scrollable
/// widget itself. For example, [Scrollable] uses a context outside the
/// [Viewport] but inside the widgets created by
/// [ScrollBehavior.buildViewportChrome].
BuildContext get notificationContext;
/// A [TickerProvider] to use when animating the scroll position.
TickerProvider get vsync;
/// The direction in which the widget scrolls.
AxisDirection get axisDirection;
/// Whether the contents of the widget should ignore [PointerEvent] inputs.
///
/// Setting this value to true prevents the use from interacting with the
/// contents of the widget with pointer events. The widget itself is still
/// interactive.
///
/// For example, if the scroll position is being driven by an animation, it
/// might be appropriate to set this value to ignore pointer events to
/// prevent the user from accidentially interacting with the contents of the
/// widget as it animates. The user will still be able to touch the widget,
/// potentially stopping the animation.
void setIgnorePointer(bool value);
/// Whether the user can drag the widget, for example to initiate a scroll.
void setCanDrag(bool value);
}
......@@ -26,6 +26,11 @@ import 'package:flutter/rendering.dart';
/// The above values are also exposed in terms of [extentBefore],
/// [extentInside], and [extentAfter], which may be more useful for use cases
/// such as scroll bars; for example, see [Scrollbar].
///
/// See also:
///
/// * [FixedScrollMetrics], which is an immutable object that implements this
/// interface.
abstract class ScrollMetrics {
/// Creates a [ScrollMetrics] that has the same properties as this object.
///
......@@ -33,16 +38,34 @@ abstract class ScrollMetrics {
/// of the current state.
ScrollMetrics cloneMetrics() => new FixedScrollMetrics.clone(this);
/// The minimum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
double get minScrollExtent;
/// The maximum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
double get maxScrollExtent;
/// The current scroll position, in logical pixels along the [axisDirection].
double get pixels;
/// The extent of the viewport along the [axisDirection].
double get viewportDimension;
/// The direction in which the scroll view scrolls.
AxisDirection get axisDirection;
/// The axis in which the scroll view scrolls.
Axis get axis => axisDirectionToAxis(axisDirection);
/// Whether the [pixels] value is outside the [minScrollExtent] and
/// [maxScrollExtent].
bool get outOfRange => pixels < minScrollExtent || pixels > maxScrollExtent;
/// Whether the [pixels] value is exactly at the [minScrollExtent] or the
/// [maxScrollExtent].
bool get atEdge => pixels == minScrollExtent || pixels == maxScrollExtent;
/// The quantity of content conceptually "above" the currently visible content
......@@ -72,8 +95,12 @@ abstract class ScrollMetrics {
}
}
/// An immutable snapshot of values associated with a [Scrollable] viewport.
///
/// For details, see [ScrollMetrics], which defines this object's interfaces.
@immutable
class FixedScrollMetrics extends ScrollMetrics {
/// Creates an immutable snapshot of values associated with a [Scrollable] viewport.
FixedScrollMetrics({
@required this.minScrollExtent,
@required this.maxScrollExtent,
......@@ -82,6 +109,7 @@ class FixedScrollMetrics extends ScrollMetrics {
@required this.axisDirection,
});
/// Creates an immutable snapshot of the given metrics.
FixedScrollMetrics.clone(ScrollMetrics parent) :
minScrollExtent = parent.minScrollExtent,
maxScrollExtent = parent.maxScrollExtent,
......
......@@ -304,7 +304,11 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
/// [ScrollDragController.onDragCanceled] for details.
@override
Drag drag(DragStartDetails details, VoidCallback onDragCanceled) {
final ScrollDragController drag = new ScrollDragController(this, details, onDragCanceled);
final ScrollDragController drag = new ScrollDragController(
delegate: this,
details: details,
onDragCanceled: onDragCanceled,
);
beginActivity(new DragScrollActivity(this, drag));
assert(_currentDrag == null);
_currentDrag = drag;
......
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