Commit 49d32d35 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Add more docs about scrolling (#9711)

parent ea96773f
......@@ -62,7 +62,7 @@ bool debugPaintBaselinesEnabled = false;
/// The color to use when painting alphabetic baselines.
Color debugPaintAlphabeticBaselineColor = _kDebugPaintAlphabeticBaselineColor;
/// The color ot use when painting ideographic baselines.
/// The color to use when painting ideographic baselines.
Color debugPaintIdeographicBaselineColor = _kDebugPaintIdeographicBaselineColor;
/// Causes each Layer to paint a box around its bounds.
......
......@@ -201,6 +201,15 @@ class ScrollController extends ChangeNotifier {
return '$runtimeType#$hashCode(${description.join(", ")})';
}
/// Add additional information to the given description for use by [toString].
///
/// This method makes it easier for subclasses to coordinate to provide a
/// high-quality [toString] implementation. The [toString] implementation on
/// the [ScrollController] base class calls [debugFillDescription] to collect
/// useful information from subclasses to incorporate into its return value.
///
/// If you override this, make sure to start your method with a call to
/// `super.debugFillDescription(description)`.
@mustCallSuper
void debugFillDescription(List<String> description) {
if (debugLabel != null)
......
......@@ -7,7 +7,7 @@ import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
/// A description of a [Scrollable]'s contents, useful for modelling the state
/// A description of a [Scrollable]'s contents, useful for modeling the state
/// of its viewport.
///
/// This class defines a current position, [pixels], and a range of values
......
......@@ -39,13 +39,41 @@ abstract class ViewportNotificationMixin extends Notification {
}
}
/// A [Notification] related to scrolling.
///
/// [Scrollable] widgets notify their ancestors about scrolling-related changes.
/// The notifications have the following lifecycle:
///
/// * A [ScrollStartNotification], which indicates that the widget has started
/// scrolling.
/// * Zero or more [ScrollUpdateNotification]s, which indicate that the widget
/// has changed its scroll position, mixed with zero or more
/// [OverscrollNotification]s, which indicate that the widget has not changed
/// its scroll position because the change would have caused its scroll
/// position to go outside its scroll bounds.
/// * Interspersed with the [ScrollUpdateNotification]s and
/// [OverscrollNotification]s are zero or more [UserScrollNotification]s,
/// which indicate that the user has changed the direciton in which they are
/// scrolling.
/// * A [ScrollEndNotification], which indicates that the widget has stopped
/// scrolling.
/// * A [UserScrollNotification], with a [UserScrollNotification.direciton] of
/// [ScrollDirection.idle].
///
/// Notifications bubble up through the tree, which means a given
/// [NotificationListener] will receive notifications for all descendant
/// [Scrollable] widgets. To focus on notifications from the nearest
/// [Scrollable] descendant, check that the [depth] property of the notification
/// is zero.
abstract class ScrollNotification extends LayoutChangedNotification with ViewportNotificationMixin {
/// Creates a notification about scrolling.
/// Initializes fields for subclasses.
ScrollNotification({
@required this.metrics,
@required this.context,
});
// A description of a [Scrollable]'s contents, useful for modeling the state
/// of its viewport.
final ScrollMetrics metrics;
/// The build context of the widget that fired this notification.
......@@ -61,13 +89,24 @@ abstract class ScrollNotification extends LayoutChangedNotification with Viewpor
}
}
/// A notification that a [Scrollable] widget has started scrolling.
///
/// See also:
///
/// * [ScrollEndNotification], which indicates that scrolling has stopped.
/// * [ScrollNotification], which describes the notification lifecycle.
class ScrollStartNotification extends ScrollNotification {
/// Creates a notification that a [Scrollable] widget has started scrolling.
ScrollStartNotification({
@required ScrollMetrics metrics,
@required BuildContext context,
this.dragDetails,
}) : super(metrics: metrics, context: context);
/// If the [Scrollable] started scrolling because of a drag, the details about
/// that drag start.
///
/// Otherwise, null.
final DragStartDetails dragDetails;
@override
......@@ -78,7 +117,17 @@ class ScrollStartNotification extends ScrollNotification {
}
}
/// A notification that a [Scrollable] widget has changed its scroll position.
///
/// See also:
///
/// * [OverscrollNotification], which indicates that a [Scrollable] widget
/// has not changed its scroll position because the change would have caused
/// its scroll position to go outside its scroll bounds.
/// * [ScrollNotification], which describes the notification lifecycle.
class ScrollUpdateNotification extends ScrollNotification {
/// Creates a notification that a [Scrollable] widget has changed its scroll
/// position.
ScrollUpdateNotification({
@required ScrollMetrics metrics,
@required BuildContext context,
......@@ -86,6 +135,10 @@ class ScrollUpdateNotification extends ScrollNotification {
this.scrollDelta,
}) : super(metrics: metrics, context: context);
/// If the [Scrollable] changed its scroll position because of a drag, the
/// details about that drag update.
///
/// Otherwise, null.
final DragUpdateDetails dragDetails;
/// The distance by which the [Scrollable] was scrolled, in logical pixels.
......@@ -100,7 +153,18 @@ class ScrollUpdateNotification extends ScrollNotification {
}
}
/// A notification that a [Scrollable] widget has not changed its scroll position
/// because the change would have caused its scroll position to go outside of
/// its scroll bounds.
///
/// See also:
///
/// * [ScrollUpdateNotification], which indicates that a [Scrollable] widget
/// has changed its scroll position.
/// * [ScrollNotification], which describes the notification lifecycle.
class OverscrollNotification extends ScrollNotification {
/// Creates a notification that a [Scrollable] widget has changed its scroll
/// position outside of its scroll bounds.
OverscrollNotification({
@required ScrollMetrics metrics,
@required BuildContext context,
......@@ -114,6 +178,10 @@ class OverscrollNotification extends ScrollNotification {
assert(velocity != null);
}
/// If the [Scrollable] overscrolled because of a drag, the details about that
/// drag update.
///
/// Otherwise, null.
final DragUpdateDetails dragDetails;
/// The number of logical pixels that the [Scrollable] avoided scrolling.
......@@ -140,13 +208,31 @@ class OverscrollNotification extends ScrollNotification {
}
}
/// A notification that a [Scrollable] widget has stopped scrolling.
///
/// See also:
///
/// * [ScrollStartNotification], which indicates that scrolling has started.
/// * [ScrollNotification], which describes the notification lifecycle.
class ScrollEndNotification extends ScrollNotification {
/// Creates a notification that a [Scrollable] widget has stopped scrolling.
ScrollEndNotification({
@required ScrollMetrics metrics,
@required BuildContext context,
this.dragDetails,
}) : super(metrics: metrics, context: context);
/// If the [Scrollable] stopped scrolling because of a drag, the details about
/// that drag end.
///
/// Otherwise, null.
///
/// If a drag ends with some residual velocity, a typical [ScrollPhysics] will
/// start a ballistic scroll, which delays the [ScrollEndNotification] until
/// the ballistic simulation completes, at which time [dragDetails] will
/// be null. If the residtual velocity is too small to trigger ballistic
/// scrolling, then the [ScrollEndNotification] will be dispatched immediately
/// and [dragDetails] will be non-null.
final DragEndDetails dragDetails;
@override
......@@ -157,13 +243,22 @@ class ScrollEndNotification extends ScrollNotification {
}
}
/// A notification that the user has changed the direction in which they are
/// scrolling.
///
/// See also:
///
/// * [ScrollNotification], which describes the notification lifecycle.
class UserScrollNotification extends ScrollNotification {
/// Creates a notification that the user has changed the direction in which
/// they are scrolling.
UserScrollNotification({
@required ScrollMetrics metrics,
@required BuildContext context,
this.direction,
}) : super(metrics: metrics, context: context);
/// The direction in which the user is scrolling.
final ScrollDirection direction;
@override
......
......@@ -15,12 +15,34 @@ import 'scroll_simulation.dart';
export 'package:flutter/physics.dart' show Tolerance;
/// Determines the physics of a [Scrollable] widget.
///
/// For example, determines how the [Scrollable] will behave when the user
/// reaches the maximum scroll extent or when the user stops scrolling.
///
/// When starting a physics [Simulation], the current scroll position and
/// velocity are used as the initial conditions for the particle in the
/// simulation. The movement of the particle in the simulation is then used to
/// determine the scroll position for the widget.
@immutable
class ScrollPhysics {
/// Creates an object with the default scroll physics.
const ScrollPhysics({ this.parent });
/// If non-null, determines the default behavior for each method.
///
/// If a subclass of [ScrollPhysics] does not override a method, that subclass
/// will inherit an implementation from this base class that defers to
/// [parent]. This mechanism lets you assemble novel combinations of
/// [ScrollPhysics] subclasses at runtime.
final ScrollPhysics parent;
/// Return a [ScrollPhysics] with the same [runtimeType] where the [parent]
/// has been replaced with the given [parent].
///
/// The returned object will combine some of the behaviors from this
/// [ScrollPhysics] instance and some of the behaviors from the given
/// [ScrollPhysics] instance.
ScrollPhysics applyTo(ScrollPhysics parent) => new ScrollPhysics(parent: parent);
/// Used by [DragScrollActivity] and other user-driven activities to
......@@ -111,6 +133,7 @@ class ScrollPhysics {
ratio: 1.1,
);
/// The spring to use for ballistic simulations.
SpringDescription get spring => parent?.spring ?? _kDefaultSpring;
/// The default accuracy to which scrolling is computed.
......@@ -121,6 +144,7 @@ class ScrollPhysics {
distance: 1.0 / ui.window.devicePixelRatio // logical pixels
);
/// The tolerance to use for ballistic simulations.
Tolerance get tolerance => parent?.tolerance ?? _kDefaultTolerance;
/// The minimum distance an input pointer drag must have moved to
......
......@@ -120,8 +120,16 @@ class ClampingScrollSimulation extends Simulation {
_distance = (velocity * _duration / _kInitialVelocityPenetration).abs();
}
/// The position of the particle at the beginning of the simulation.
final double position;
/// The velocity at which the particle is traveling at the beginning of the
/// simulation.
final double velocity;
/// The amount of friction the particle experiences as it travels.
///
/// The more friction the particle experiences, the sooner it stops.
final double friction;
double _duration;
......
......@@ -24,9 +24,53 @@ import 'viewport.dart';
export 'package:flutter/physics.dart' show Tolerance;
/// Signature used by [Scrollable] to build the viewport through which the
/// scrollable content is displayed.
typedef Widget ViewportBuilder(BuildContext context, ViewportOffset position);
/// A widget that scrolls.
///
/// [Scrollable] implements the interaction model for a scrollable widget,
/// including gesture recognition, but does not have an opinion about how the
/// viewport, which actually displays the children, is constructed.
///
/// It's rare to construct a [Scrollable] directly. Instead, consider [ListView]
/// or [GridView], which combine scrolling, viewporting, and a layout model. To
/// combine layout models (or to use a custom layout mode), consider using
/// [CustomScrollView].
///
/// The static [Scrollable.of] and [Scrollable.ensureVisible] functions are
/// often used to interact with the [Scrollable] widget inside a [ListView] or
/// a [GridView].
///
/// To further customize scrolling behavior with a [Scrollable]:
///
/// 1. You can provide a [viewportBuilder] to customize the child model. For
/// example, [SingleChildScrollView] uses a viewport that displays a single
/// box child whereas [CustomScrollView] uses a [Viewport] or a
/// [ShrinkWrappingViewport], both of which display a list of slivers.
///
/// 2. You can provide a custom [ScrollController] that creates a custom
/// [ScrollPosition] subclass. For example, [PageView] uses a
/// [PageController], which creates a page-oriented scroll position subclass
/// that keeps the same page visible when the [Scrollable] resizes.
///
/// See also:
///
/// * [ListView], which is a commonly used [ScrollView] that displays a
/// scrolling, linear list of child widgets.
/// * [PageView], which is a scrolling list of child widgets that are each the
/// size of the viewport.
/// * [GridView], which is a [ScrollView] that displays a scrolling, 2D array
/// of child widgets.
/// * [CustomScrollView], which is a [ScrollView] that creates custom scroll
/// effects using slivers.
/// * [SingleChildScrollView], which is a scrollable widget that has a single
/// child.
class Scrollable extends StatefulWidget {
/// Creates a widget that scrolls.
///
/// The [axisDirection] and [viewportBuilder] arguments must not be null.
const Scrollable({
Key key,
this.axisDirection: AxisDirection.down,
......@@ -37,14 +81,50 @@ class Scrollable extends StatefulWidget {
assert(viewportBuilder != null),
super (key: key);
/// The direction in which this widget scrolls.
///
/// For example, if the [axisDirection] is [AxisDirection.down], increasing
/// the scroll position will cause content below the bottom of the viewport to
/// become visible through the viewport. Similarly, if [axisDirection] is
/// [AxisDirection.right], increasing the scroll position will cause content
/// beyond the right edge of the viewport to become visible through the
/// viewport.
///
/// Defaults to [AxisDirection.down].
final AxisDirection axisDirection;
/// An object that can be used to control the position to which this widget is
/// scrolled.
///
/// See also:
///
/// * [ensureVisible], which animates the scroll position to reveal a given
/// [BuildContext].
final ScrollController controller;
/// How the widgets should respond to user input.
///
/// For example, determines how the widget continues to animate after the
/// user stops dragging the scroll view.
///
/// Defaults to matching platform conventions.
final ScrollPhysics physics;
/// Builds the viewport through which the scrollable content is displayed.
///
/// A typical viewport uses the given [ViewportOffset] to determine which part
/// of its content is actually visible through the viewport.
///
/// See also:
///
/// * [Viewport], which is a viewport that displays a list of slivers.
/// * [ShrinkWrappingViewport], which is a viewport that displays a list of
/// slivers and sizes itself based on the size of the slivers.
final ViewportBuilder viewportBuilder;
/// The axis along which the scroll view scrolls.
///
/// Determined by the [axisDirection].
Axis get axis => axisDirectionToAxis(axisDirection);
@override
......
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