Commit f53d0fec authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Add some documentation for RenderSliver. (#10272)

parent 51ba6b37
......@@ -763,18 +763,141 @@ String _debugCompareFloats(String labelA, double valueA, String labelB, double v
'apply the min() or max() functions, or the clamp() method, to the $labelB? ';
}
// ///
// /// ## Writing a RenderSliver subclass
// ///
// /// ### Painting
// ///
// /// The [paint] method is called with an [Offset] to the top-left corner of the
// /// sliver, _regardless of the axis direction_.
// ///
// /// ### childScrollOffset
// ///
// /// If the subclass positions children anywhere other than at scroll offset
// /// 0.0, you need to override [childScrollOffset]...
/// Base class for the render objects that implement scroll effects in viewports.
///
/// A [RenderViewport] has a list of child slivers. Each sliver — literally a
/// slice of the viewport's contents — is laid out in turn, covering the
/// viewport in the process. (Every sliver is laid out each time, including
/// those that have zero extent because they are "scrolled off" or are beyond
/// the end of the viewport.)
///
/// Slivers participate in the _sliver protocol_, wherein during [layout] each
/// sliver receives a [SliverConstraints] object and computes a corresponding
/// [SliverGeometry] that describes where it fits in the viewport. This is
/// analogous to the box protocol used by [RenderBox], which gets a
/// [BoxConstraints] as input and computes a [Size].
///
/// Slivers have a leading edge, which is where the position described by
/// [SliverConstraints.scrollOffset] for this sliver begins. Slivers have
/// several dimensions, the primary of which is [SliverGeometry.paintExtent],
/// which describes the extent of the sliver along the main axis, starting from
/// the leading edge, reaching either the end of the viewport or the end of the
/// sliver, whichever comes first.
///
/// Slivers can change dimensions based on the changing constraints in a
/// non-linear fashion, to achieve various scroll effects. For example, the
/// various [RenderSliverPersistentHeader] subclasses, on which [SliverAppBar]
/// is based, achieve effects such as staying visible despite the scroll offset,
/// or reappearing at different offsets based on the user's scroll direction
/// ([SliverConstraints.userScrollDirection]).
///
/// ## Writing a RenderSliver subclass
///
/// Slivers can have sliver children, or children from another coordinate
/// system, typically box children. (For details on the box protocol, see
/// [RenderBox].) Slivers can also have different child models, typically having
/// either one child, or a list of children.
///
/// ### Examples of slivers
///
/// A good example of a sliver with a single child that is also itself a sliver
/// is [RenderSliverPadding], which indents its child. A sliver-to-sliver render
/// object such as this must construct a [SliverConstraints] object for its
/// child, then must take its child's [SliverGeometry] and use it to form its
/// own [geometry].
///
/// The other common kind of one-child sliver is a sliver that has a single
/// [RenderBox] child. An example of that would be [RenderSliverToBoxAdapter],
/// which lays out a single box and sizes itself around the box. Such a sliver
/// must use its [SliverConstraints] to create a [BoxConstraints] for the
/// child, lay the child out (using the child's [layout] method), and then use
/// the child's [RenderBox.size] to generate the sliver's [SliverGeometry].
///
/// The most common kind of sliver though is one with multiple children. The
/// most straight-forward example of this is [RenderSliverList], which arranges
/// its children one after the other in the main axis direction. As with the
/// one-box-child sliver case, it uses its [constraints] to create a
/// [BoxConstraints] for the children, and then it uses the aggregate
/// information from all its children to generate its [geometry]. Unlike the
/// one-child cases, however, it is judicious in which children it actually lays
/// out (and later paints). If the scroll offset is 1000 pixels, and it
/// previously determined that the first three children are each 400 pixels
/// tall, then it will skip the first two and start the layout with its third
/// child.
///
/// ### Layout
///
/// As they are laid out, slivers decide their [geometry], which includes their
/// size ([SliverGeometry.paintExtent]) and the position of the next sliver
/// ([SliverGeometry.layoutExtent]), as well as the position of each of their
/// children, based on the input [constraints] from the viewport such as the
/// scroll offset ([SliverConstraints.scrollOffset]).
///
/// For example, a sliver that just paints a box 100 pixels high would say its
/// [SliverGeometry.paintExtent] was 100 pixels when the scroll offset was zero,
/// but would say its [SliverGeometry.paintExtent] was 25 pixels when the scroll
/// offset was 75 pixels, and would say it was zero when the scroll offset was
/// 100 pixels or more. (This is assuming that
/// [SliverConstraints.remainingPaintExtent] was more than 100 pixels.)
///
/// The various dimensions that are provided as input to this system are in the
/// [constraints]. They are described in detail in the documentation for the
/// [SliverConstraints] class.
///
/// The [performLayout] function must take these [constraints] and create a
/// [SliverGeometry] object that it must then assign to the [geometry] property.
/// The different dimensions of the geometry that can be configured are
/// described in detail in the documentation for the [SliverGeometry] class.
///
/// ### Painting
///
/// In addition to implementing layout, a sliver must also implement painting.
/// This is achieved by overriding the [paint] method.
///
/// The [paint] method is called with an [Offset] from the [Canvas] origin to
/// the top-left corner of the sliver, _regardless of the axis direction_.
///
/// Subclasses should also override [applyPaintTransform] to provide the
/// [Matrix4] describing the position of each child relative to the sliver.
/// (This is used by, among other things, the accessibility layer, to determine
/// the bounds of the child.)
///
/// ### Hit testing
///
/// To implement hit testing, either override the [hitTestSelf] and
/// [hitTestChildren] methods, or, for more complex cases, instead override the
/// [hitTest] method directly.
///
/// To actually react to pointer events, the [handleEvent] method may be
/// implemented. By default it does nothing. (Typically gestures are handled by
/// widgets in the box protocol, not by slivers directly.)
///
/// ### Helper methods
///
/// There are a number of methods that a sliver should implement which will make
/// the other methods easier to implement. Each method listed below has detailed
/// documentation. In addition, the [RenderSliverHelpers] class can be used to
/// mix in some helpful methods.
///
/// #### childScrollOffset
///
/// If the subclass positions children anywhere other than at scroll offset
/// zero, it should override [childScrollOffset]. For example,
/// [RenderSliverList] and [RenderSliverGrid] override this method, but
/// [RenderSliverToBoxAdapter] does not.
///
/// This is used by, among other things, [Scrollable.ensureVisible].
///
/// #### childMainAxisPosition
///
/// Subclasses should implement [childMainAxisPosition] to describe where their
/// children are positioned.
///
/// #### childCrossAxisPosition
///
/// If the subclass positions children in the cross-axis at a position other
/// than zero, then it should override [childCrossAxisPosition]. For example
/// [RenderSliverGrid] overrides this method.
abstract class RenderSliver extends RenderObject {
// layout input
@override
......@@ -925,6 +1048,12 @@ abstract class RenderSliver extends RenderObject {
/// vertical (i.e. the [SliverConstraints.axisDirection] is either
/// [AxisDirection.right] or [AxisDirection.left]), then the
/// `crossAxisPosition` is a distance from the top edge of the sliver.
///
/// ## Implementing hit testing for slivers
///
/// The most straight-forward way to implement hit testing for a new sliver
/// render object is to override its [hitTestSelf] and [hitTestChildren]
/// methods.
bool hitTest(HitTestResult result, { @required double mainAxisPosition, @required double crossAxisPosition }) {
if (mainAxisPosition >= 0.0 && mainAxisPosition < geometry.hitTestExtent &&
crossAxisPosition >= 0.0 && crossAxisPosition < constraints.crossAxisExtent) {
......@@ -980,6 +1109,8 @@ abstract class RenderSliver extends RenderObject {
/// sliver always paints the same amount but consumes a scroll offset extent
/// that is proportional to the [SliverConstraints.scrollOffset], then this
/// function's results will not be consistent.
// This could be a static method but isn't, because it would be less convenient
// to call it from subclasses if it was.
double calculatePaintOffset(SliverConstraints constraints, { @required double from, @required double to }) {
assert(from <= to);
final double a = constraints.scrollOffset;
......@@ -1001,8 +1132,6 @@ abstract class RenderSliver extends RenderObject {
/// [SliverConstraints.scrollOffset] and
/// [SliverLogicalParentData.layoutOffset].
///
/// Calling this for a child that is not visible is not valid.
///
/// For children that are [RenderSliver]s, the leading edge of the _child_
/// will be the leading _visible_ edge of the child, not the part of the child
/// that would locally be a scroll offset 0.0. For children that are not
......@@ -1010,14 +1139,12 @@ abstract class RenderSliver extends RenderObject {
/// to the edge of the box, since those boxes do not know how to handle being
/// scrolled.
///
/// This is used by [RenderSliverHelpers.hitTestBoxChild]. If you do not use
/// the [RenderSliverHelpers] mixin and do not call this method yourself, you
/// do not need to implement this method.
///
/// This method differs from [childScrollOffset] in that
/// [childMainAxisPosition] gives the distance from the leading _visible_ edge
/// of the sliver whereas [childScrollOffset] gives the distance from sliver's
/// zero scroll offset.
/// of the sliver whereas [childScrollOffset] gives the distance from the
/// sliver's zero scroll offset.
///
/// Calling this for a child that is not visible is not valid.
@protected
double childMainAxisPosition(covariant RenderObject child) {
assert(() {
......@@ -1027,13 +1154,16 @@ abstract class RenderSliver extends RenderObject {
}
/// Returns the distance along the cross axis from the zero of the cross axis
/// to the nearest side of the given child.
/// in this sliver's [paint] coordinate space to the nearest side of the given
/// child.
///
/// For example, if the [constraints] describe this sliver as having an axis
/// direction of [AxisDirection.down], then this is the distance from the left
/// of the sliver to the left of the child. Similarly, if the [constraints]
/// describe this sliver as having an axis direction of [AxisDirection.up],
/// then this is value is the same.
/// then this is value is the same. If the axis direction is
/// [AxisDirection.left] or [AxisDirection.right], then it is the distance
/// from the top of the sliver to the top of the child.
///
/// Calling this for a child that is not visible is not valid.
@protected
......@@ -1062,6 +1192,8 @@ abstract class RenderSliver extends RenderObject {
/// This returns a [Size] with dimensions relative to the leading edge of the
/// sliver, specifically the same offset that is given to the [paint] method.
/// This means that the dimensions may be negative.
///
/// This is only valid after [layout] has completed.
@protected
Size getAbsoluteSizeRelativeToOrigin() {
assert(geometry != null);
......@@ -1183,6 +1315,7 @@ abstract class RenderSliver extends RenderObject {
});
}
// This override exists only to change the type of the second argument.
@override
void handleEvent(PointerEvent event, SliverHitTestEntry entry) { }
......
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