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 ...@@ -1261,6 +1261,10 @@ abstract class RenderSliverSingleBoxAdapter extends RenderSliver with RenderObje
child.parentData = new SliverPhysicalParentData(); 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) { void setChildParentData(RenderObject child, SliverConstraints constraints, SliverGeometry geometry) {
final SliverPhysicalParentData childParentData = child.parentData; final SliverPhysicalParentData childParentData = child.parentData;
assert(constraints.axisDirection != null); assert(constraints.axisDirection != null);
......
...@@ -24,7 +24,8 @@ import 'scroll_view.dart'; ...@@ -24,7 +24,8 @@ import 'scroll_view.dart';
import 'sliver.dart'; import 'sliver.dart';
import 'ticker_provider.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 { class NestedScrollView extends StatefulWidget {
NestedScrollView({ NestedScrollView({
...@@ -49,7 +50,7 @@ class NestedScrollView extends StatefulWidget { ...@@ -49,7 +50,7 @@ class NestedScrollView extends StatefulWidget {
final ScrollPhysics physics; final ScrollPhysics physics;
final NestedScrollViewOuterSliversBuilder headerSliverBuilder; final NestedScrollViewHeaderSliversBuilder headerSliverBuilder;
final Widget body; final Widget body;
...@@ -415,9 +416,9 @@ class _NestedScrollCoorindator implements ScrollActivityDelegate { ...@@ -415,9 +416,9 @@ class _NestedScrollCoorindator implements ScrollActivityDelegate {
Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) { Drag drag(DragStartDetails details, VoidCallback dragCancelCallback) {
final ScrollDragController drag = new ScrollDragController( final ScrollDragController drag = new ScrollDragController(
this, delegate: this,
details, details: details,
dragCancelCallback, onDragCanceled: dragCancelCallback,
); );
beginActivity( beginActivity(
new DragScrollActivity(_outerPosition, drag), new DragScrollActivity(_outerPosition, drag),
......
...@@ -7,7 +7,17 @@ import 'package:flutter/foundation.dart'; ...@@ -7,7 +7,17 @@ import 'package:flutter/foundation.dart';
import 'framework.dart'; import 'framework.dart';
import 'scroll_controller.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 { class PrimaryScrollController extends InheritedWidget {
/// Creates a widget that associates a [ScrollController] with a subtree.
const PrimaryScrollController({ const PrimaryScrollController({
Key key, Key key,
@required this.controller, @required this.controller,
...@@ -15,14 +25,21 @@ class PrimaryScrollController extends InheritedWidget { ...@@ -15,14 +25,21 @@ class PrimaryScrollController extends InheritedWidget {
}) : assert(controller != null), }) : assert(controller != null),
super(key: key, child: child); super(key: key, child: child);
/// Creates a subtree without an associated [ScrollController].
const PrimaryScrollController.none({ const PrimaryScrollController.none({
Key key, Key key,
@required Widget child @required Widget child
}) : controller = null, }) : controller = null,
super(key: key, child: child); super(key: key, child: child);
/// The [ScrollController] associated with the subtree.
final ScrollController controller; 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) { static ScrollController of(BuildContext context) {
final PrimaryScrollController result = context.inheritFromWidgetOfExactType(PrimaryScrollController); final PrimaryScrollController result = context.inheritFromWidgetOfExactType(PrimaryScrollController);
return result?.controller; return result?.controller;
......
...@@ -17,13 +17,37 @@ import 'scroll_metrics.dart'; ...@@ -17,13 +17,37 @@ import 'scroll_metrics.dart';
import 'scroll_notification.dart'; import 'scroll_notification.dart';
import 'ticker_provider.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 { abstract class ScrollActivityDelegate {
/// The direction in which the scroll view scrolls.
AxisDirection get axisDirection; 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); 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); void applyUserOffset(double delta);
/// Terminate the current activity and start an idle activity.
void goIdle(); void goIdle();
/// Terminate the current activity and start a ballistic activity with the
/// given velocity.
void goBallistic(double velocity); void goBallistic(double velocity);
} }
...@@ -31,11 +55,13 @@ abstract class ScrollActivityDelegate { ...@@ -31,11 +55,13 @@ abstract class ScrollActivityDelegate {
/// ///
/// See also: /// See also:
/// ///
/// * [ScrollPositionWithSingleContext], which uses [ScrollActivity] objects to /// * [ScrollPosition], which uses [ScrollActivity] objects to manage the
/// manage the [ScrollPosition] of a [Scrollable]. /// [ScrollPosition] of a [Scrollable].
abstract class ScrollActivity { abstract class ScrollActivity {
/// Initializes [delegate] for subclasses.
ScrollActivity(this._delegate); ScrollActivity(this._delegate);
/// The delegate that this activity will use to actuate the scroll view.
ScrollActivityDelegate get delegate => _delegate; ScrollActivityDelegate get delegate => _delegate;
ScrollActivityDelegate _delegate; ScrollActivityDelegate _delegate;
...@@ -58,30 +84,43 @@ abstract class ScrollActivity { ...@@ -58,30 +84,43 @@ abstract class ScrollActivity {
/// [ScrollActivityDelegate.goBallistic]. /// [ScrollActivityDelegate.goBallistic].
void resetActivity() { } void resetActivity() { }
/// Dispatch a [ScrollStartNotification] with the given metrics.
void dispatchScrollStartNotification(ScrollMetrics metrics, BuildContext context) { void dispatchScrollStartNotification(ScrollMetrics metrics, BuildContext context) {
new ScrollStartNotification(metrics: metrics, context: context).dispatch(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) { void dispatchScrollUpdateNotification(ScrollMetrics metrics, BuildContext context, double scrollDelta) {
new ScrollUpdateNotification(metrics: metrics, context: context, scrollDelta: scrollDelta).dispatch(context); 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) { void dispatchOverscrollNotification(ScrollMetrics metrics, BuildContext context, double overscroll) {
new OverscrollNotification(metrics: metrics, context: context, overscroll: overscroll).dispatch(context); 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) { void dispatchScrollEndNotification(ScrollMetrics metrics, BuildContext context) {
new ScrollEndNotification(metrics: metrics, context: context).dispatch(context); new ScrollEndNotification(metrics: metrics, context: context).dispatch(context);
} }
/// Called when the user touches the scroll view that is performing this activity.
void didTouch() { } void didTouch() { }
/// Called when the scroll view that is performing this activity changes its metrics.
void applyNewDimensions() { } void applyNewDimensions() { }
/// Whether the scroll view should ignore pointer events while performing this
/// activity.
bool get shouldIgnorePointer; 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; bool get isScrolling;
/// Called when the scroll view stops performing this activity.
@mustCallSuper @mustCallSuper
void dispose() { void dispose() {
_delegate = null; _delegate = null;
...@@ -91,7 +130,11 @@ abstract class ScrollActivity { ...@@ -91,7 +130,11 @@ abstract class ScrollActivity {
String toString() => '$runtimeType'; 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 { class IdleScrollActivity extends ScrollActivity {
/// Creates a scroll activity that does nothing.
IdleScrollActivity(ScrollActivityDelegate delegate) : super(delegate); IdleScrollActivity(ScrollActivityDelegate delegate) : super(delegate);
@override @override
...@@ -106,16 +149,31 @@ class IdleScrollActivity extends ScrollActivity { ...@@ -106,16 +149,31 @@ class IdleScrollActivity extends ScrollActivity {
bool get isScrolling => false; 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 { class ScrollDragController implements Drag {
ScrollDragController( /// Creates an object that scrolls a scroll view as the user drags their
ScrollActivityDelegate delegate, /// finger across the screen.
DragStartDetails details, ///
/// The [delegate] and `details` arguments must not be null.
ScrollDragController({
@required ScrollActivityDelegate delegate,
@required DragStartDetails details,
this.onDragCanceled, 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 get delegate => _delegate;
ScrollActivityDelegate _delegate; ScrollActivityDelegate _delegate;
/// Called when [dispose] is called.
final VoidCallback onDragCanceled; final VoidCallback onDragCanceled;
bool get _reversed => axisDirectionIsReversed(delegate.axisDirection); bool get _reversed => axisDirectionIsReversed(delegate.axisDirection);
...@@ -159,6 +217,7 @@ class ScrollDragController implements Drag { ...@@ -159,6 +217,7 @@ class ScrollDragController implements Drag {
delegate.goBallistic(0.0); delegate.goBallistic(0.0);
} }
/// Called when the delegate is no longer sending events to this object.
@mustCallSuper @mustCallSuper
void dispose() { void dispose() {
_lastDetails = null; _lastDetails = null;
...@@ -166,11 +225,22 @@ class ScrollDragController implements Drag { ...@@ -166,11 +225,22 @@ class ScrollDragController implements Drag {
onDragCanceled(); onDragCanceled();
} }
/// The most recently observed [DragStartDetails], [DragUpdateDetails], or
/// [DragEndDetails] object.
dynamic get lastDetails => _lastDetails; dynamic get lastDetails => _lastDetails;
dynamic _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 { class DragScrollActivity extends ScrollActivity {
/// Creates an activity for when the user drags their finger across the
/// screen.
DragScrollActivity( DragScrollActivity(
ScrollActivityDelegate delegate, ScrollActivityDelegate delegate,
ScrollDragController controller, ScrollDragController controller,
...@@ -228,9 +298,23 @@ class DragScrollActivity extends ScrollActivity { ...@@ -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 { class BallisticScrollActivity extends ScrollActivity {
// /// /// Creates an activity that animates a scroll view based on a [simulation].
// /// The velocity should be in logical pixels per second. ///
/// The [delegate], [simulation], and [vsync] arguments must not be null.
BallisticScrollActivity( BallisticScrollActivity(
ScrollActivityDelegate delegate, ScrollActivityDelegate delegate,
Simulation simulation, Simulation simulation,
...@@ -245,6 +329,8 @@ class BallisticScrollActivity extends ScrollActivity { ...@@ -245,6 +329,8 @@ class BallisticScrollActivity extends ScrollActivity {
.whenComplete(_end); // won't trigger if we dispose _controller first .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; double get velocity => _controller.velocity;
AnimationController _controller; AnimationController _controller;
...@@ -271,8 +357,8 @@ class BallisticScrollActivity extends ScrollActivity { ...@@ -271,8 +357,8 @@ class BallisticScrollActivity extends ScrollActivity {
/// Move the position to the given location. /// Move the position to the given location.
/// ///
/// If the new position was fully applied, return true. /// If the new position was fully applied, returns true. If there was any
/// If there was any overflow, return false. /// overflow, returns false.
/// ///
/// The default implementation calls [ScrollActivityDelegate.setPixels] /// The default implementation calls [ScrollActivityDelegate.setPixels]
/// and returns true if the overflow was zero. /// and returns true if the overflow was zero.
...@@ -308,7 +394,20 @@ class BallisticScrollActivity extends ScrollActivity { ...@@ -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 { 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( DrivenScrollActivity(
ScrollActivityDelegate delegate, { ScrollActivityDelegate delegate, {
@required double from, @required double from,
...@@ -336,8 +435,15 @@ class DrivenScrollActivity extends ScrollActivity { ...@@ -336,8 +435,15 @@ class DrivenScrollActivity extends ScrollActivity {
Completer<Null> _completer; Completer<Null> _completer;
AnimationController _controller; 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; 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; double get velocity => _controller.velocity;
@override @override
......
...@@ -8,11 +8,44 @@ import 'package:flutter/rendering.dart'; ...@@ -8,11 +8,44 @@ import 'package:flutter/rendering.dart';
import 'framework.dart'; import 'framework.dart';
import 'ticker_provider.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 { 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; BuildContext get notificationContext;
/// A [TickerProvider] to use when animating the scroll position.
TickerProvider get vsync; TickerProvider get vsync;
/// The direction in which the widget scrolls.
AxisDirection get axisDirection; 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); void setIgnorePointer(bool value);
/// Whether the user can drag the widget, for example to initiate a scroll.
void setCanDrag(bool value); void setCanDrag(bool value);
} }
...@@ -26,6 +26,11 @@ import 'package:flutter/rendering.dart'; ...@@ -26,6 +26,11 @@ import 'package:flutter/rendering.dart';
/// The above values are also exposed in terms of [extentBefore], /// The above values are also exposed in terms of [extentBefore],
/// [extentInside], and [extentAfter], which may be more useful for use cases /// [extentInside], and [extentAfter], which may be more useful for use cases
/// such as scroll bars; for example, see [Scrollbar]. /// such as scroll bars; for example, see [Scrollbar].
///
/// See also:
///
/// * [FixedScrollMetrics], which is an immutable object that implements this
/// interface.
abstract class ScrollMetrics { abstract class ScrollMetrics {
/// Creates a [ScrollMetrics] that has the same properties as this object. /// Creates a [ScrollMetrics] that has the same properties as this object.
/// ///
...@@ -33,16 +38,34 @@ abstract class ScrollMetrics { ...@@ -33,16 +38,34 @@ abstract class ScrollMetrics {
/// of the current state. /// of the current state.
ScrollMetrics cloneMetrics() => new FixedScrollMetrics.clone(this); ScrollMetrics cloneMetrics() => new FixedScrollMetrics.clone(this);
/// The minimum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
double get minScrollExtent; double get minScrollExtent;
/// The maximum in-range value for [pixels].
///
/// The actual [pixels] value might be [outOfRange].
double get maxScrollExtent; double get maxScrollExtent;
/// The current scroll position, in logical pixels along the [axisDirection].
double get pixels; double get pixels;
/// The extent of the viewport along the [axisDirection].
double get viewportDimension; double get viewportDimension;
/// The direction in which the scroll view scrolls.
AxisDirection get axisDirection; AxisDirection get axisDirection;
/// The axis in which the scroll view scrolls.
Axis get axis => axisDirectionToAxis(axisDirection); Axis get axis => axisDirectionToAxis(axisDirection);
/// Whether the [pixels] value is outside the [minScrollExtent] and
/// [maxScrollExtent].
bool get outOfRange => pixels < minScrollExtent || pixels > 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; bool get atEdge => pixels == minScrollExtent || pixels == maxScrollExtent;
/// The quantity of content conceptually "above" the currently visible content /// The quantity of content conceptually "above" the currently visible content
...@@ -72,8 +95,12 @@ abstract class ScrollMetrics { ...@@ -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 @immutable
class FixedScrollMetrics extends ScrollMetrics { class FixedScrollMetrics extends ScrollMetrics {
/// Creates an immutable snapshot of values associated with a [Scrollable] viewport.
FixedScrollMetrics({ FixedScrollMetrics({
@required this.minScrollExtent, @required this.minScrollExtent,
@required this.maxScrollExtent, @required this.maxScrollExtent,
...@@ -82,6 +109,7 @@ class FixedScrollMetrics extends ScrollMetrics { ...@@ -82,6 +109,7 @@ class FixedScrollMetrics extends ScrollMetrics {
@required this.axisDirection, @required this.axisDirection,
}); });
/// Creates an immutable snapshot of the given metrics.
FixedScrollMetrics.clone(ScrollMetrics parent) : FixedScrollMetrics.clone(ScrollMetrics parent) :
minScrollExtent = parent.minScrollExtent, minScrollExtent = parent.minScrollExtent,
maxScrollExtent = parent.maxScrollExtent, maxScrollExtent = parent.maxScrollExtent,
......
...@@ -304,7 +304,11 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc ...@@ -304,7 +304,11 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
/// [ScrollDragController.onDragCanceled] for details. /// [ScrollDragController.onDragCanceled] for details.
@override @override
Drag drag(DragStartDetails details, VoidCallback onDragCanceled) { 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)); beginActivity(new DragScrollActivity(this, drag));
assert(_currentDrag == null); assert(_currentDrag == null);
_currentDrag = drag; _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