diff --git a/packages/flutter/lib/src/widgets/scroll_view.dart b/packages/flutter/lib/src/widgets/scroll_view.dart index a82941ae4e..549add3432 100644 --- a/packages/flutter/lib/src/widgets/scroll_view.dart +++ b/packages/flutter/lib/src/widgets/scroll_view.dart @@ -15,7 +15,35 @@ import 'scrollable.dart'; import 'sliver.dart'; import 'viewport.dart'; +/// A widget that scrolls. +/// +/// Scrollable widgets consist of three pieces: +/// +/// 1. A [Scrollable] widget, which listens for various user gestures and +/// implements the interaction design for scrolling. +/// 2. A viewport widget, such as [Viewport] or [ShrinkWrappingViewport], which +/// implements the visual design for scrolling by displaying only a portion +/// of the widgets inside the scroll view. +/// 3. One or more slivers, which are widgets that can be composed to created +/// various scrolling effects, such as lists, grids, and expanding headers. +/// +/// [ScrollView] helps orchestrate these pieces by creating the [Scrollable] and +/// the viewport and defering to its subclass to create the slivers. +/// +/// 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. abstract class ScrollView extends StatelessWidget { + /// Creates a widget that scrolls. + /// + /// If the [primary] argument is true, the [controller] must be null. ScrollView({ Key key, this.scrollDirection: Axis.vertical, @@ -35,10 +63,30 @@ abstract class ScrollView extends StatelessWidget { ); } + /// The axis along which the scroll view scrolls. + /// + /// Defaults to [Axis.vertical]. final Axis scrollDirection; + /// Whether the scroll view scrolls in the reading direction in the + /// [scrollDirection]. + /// + /// For example, if the reading direction is left-to-right and + /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from + /// left to right when [reverse] is false and from right to left when + /// [reverse] is true. + /// + /// Similarly, if [scrollDirection] is [Axis.vertical], then scroll view + /// scrolls from top to bottom when [reverse] is false and from bottom to top + /// when [reverse] is true. + /// + /// Defaults to false. final bool reverse; + /// An object that can be used to control the position to which this scroll + /// view is scrolled. + /// + /// Must be null if [primary] is true. final ScrollController controller; /// Whether this is the primary scroll view associated with the parent @@ -47,14 +95,40 @@ abstract class ScrollView extends StatelessWidget { /// On iOS, this identifies the scroll view that will scroll to top in /// response to a tap in the status bar. /// - /// Defaults to true when `scrollDirection` is vertical and `controller` is - /// not specified. + /// Defaults to true when [scrollDirection] is [Axis.vertical] and + /// [controller] is null. final bool primary; + /// How the scroll view should respond to user input. + /// + /// For example, determines how the scroll view continues to animate after the + /// user stops dragging the scroll view. + /// + /// Defaults to matching platform conventions. final ScrollPhysics physics; + /// Whether the extent of the scroll view in the [scrollDirection] should be + /// determined by the contents being viewed. + /// + /// If the scroll view does not shrink wrap, then the scroll view will expand + /// to the maximum allowed size in the [scrollDirection]. If the scroll view + /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must + /// be true. + /// + /// Shrink wrapping the content of the scroll view is significantly more + /// expensive than expanding to the maximum allowed size because the content + /// can expand and contract during scrolling, which means the size of the + /// scroll view needs to be recomputed whenever the scroll position changes. + /// + /// Defaults to false. final bool shrinkWrap; + /// Returns the [AxisDirection] in which the scroll view scrolls. + /// + /// Combines the [scrollDirection] with the [reverse] boolean to obtain the + /// concrete [AxisDirection]. + /// + /// In the future, this function will also consider the reading direction. @protected AxisDirection getDirection(BuildContext context) { // TODO(abarth): Consider reading direction. @@ -67,6 +141,8 @@ abstract class ScrollView extends StatelessWidget { return null; } + /// Subclasses should override this method to build the slivers for the inside + /// of the viewport. @protected List buildSlivers(BuildContext context); @@ -120,7 +196,28 @@ abstract class ScrollView extends StatelessWidget { } } +/// A [ScrollView] that creates custom scroll effects using slivers. +/// +/// A [CustomScrollView] lets you supply [slivers] directly to create various +/// scrolling effects, such as lists, grids, and expanding headers. For example, +/// to create a scroll view that contains an expanding app bar followed by a +/// list and a grid, use a list of three slivers: [SliverAppBar], [SliverList], +/// and [SliverGrid]. +/// +/// See also: +/// +/// * [SliverList], which is a sliver that displays linear list of children. +/// * [SliverFixedExtentList], which is a more efficient sliver that displays +/// linear list of children that have the same extent along the scroll axis. +/// * [SliverGrid], which is a sliver that displays a 2D array of children. +/// * [SliverPadding], which is a sliver that adds blank space around another +/// sliver. +/// * [SliverAppBar], which is a sliver that displays a header that can expand +/// and float as the scroll view scrolls. class CustomScrollView extends ScrollView { + /// Creates a [ScrollView] that creates custom scroll effects using slivers. + /// + /// If the [primary] argument is true, the [controller] must be null. CustomScrollView({ Key key, Axis scrollDirection: Axis.vertical, @@ -140,13 +237,25 @@ class CustomScrollView extends ScrollView { shrinkWrap: shrinkWrap, ); + /// The slivers to place inside the viewport. final List slivers; @override List buildSlivers(BuildContext context) => slivers; } +/// A [ScrollView] uses a single child layout model. +/// +/// See also: +/// +/// * [ListView], which is a [BoxScrollView] that uses a linear layout model. +/// * [GridView], which is a [BoxScrollView] that uses a 2D layout model. +/// * [CustomScrollView], which can combine multiple child layout models into a +/// single scroll view. abstract class BoxScrollView extends ScrollView { + /// Creates a [ScrollView] uses a single child layout model. + /// + /// If the [primary] argument is true, the [controller] must be null. BoxScrollView({ Key key, Axis scrollDirection: Axis.vertical, @@ -166,6 +275,7 @@ abstract class BoxScrollView extends ScrollView { shrinkWrap: shrinkWrap, ); + /// The amount of space by which to inset the children. final EdgeInsets padding; @override @@ -176,6 +286,7 @@ abstract class BoxScrollView extends ScrollView { return [ sliver ]; } + /// Subclasses should override this method to build the layout model. @protected Widget buildChildLayout(BuildContext context); @@ -187,15 +298,52 @@ abstract class BoxScrollView extends ScrollView { } } -/// A scrollable list of boxes. -// TODO(ianh): More documentation here. +/// A scrollable, linear list of widgets. +/// +/// [ListView] is the most commonly used scrolling widget. It displays its +/// children one after another in the scroll direction. In the cross axis, the +/// children are required to fill the [ListView]. +/// +/// If non-null, the [itemExtent] forces the children to have the given extent +/// in the scroll direction. Specifying an [itemExtent] is more efficient than +/// letting the children determine their own extent because the scrolling +/// machinery can make use of the foreknowledge of the children's extent to save +/// work, for example when the scroll position changes drastically. +/// +/// There are three options for constructing a [ListView]: +/// +/// 1. The default constuctor takes an explict [List] of children. This +/// constructor is appropriate for list views with a small number of +/// children because constructing the [List] requires doing work for every +/// child that could possibly be displayed in the list view instead of just +/// those children that are actually visible. +/// +/// 2. The [ListView.builder] takes an [IndexedWidgetBuilder], which builds the +/// children on demand. This constructor is appropriate for list views with +/// a large (or infinite) number of children because the builder is called +/// only for those children that are actually visible. +/// +/// 3. The [ListView.custom] takes a [SliverChildDelegate], which provides the +/// ability to customize additional aspects of the child model. For example, +/// a [SliverChildDelegate] can control the algorithm used to estimate the +/// size of children that are not actually visible. /// /// See also: /// -/// * [SingleChildScrollView], when you need to make a single child scrollable. -/// * [GridView], for a scrollable grid of boxes. -/// * [PageView], for a scrollable that works page by page. +/// * [SingleChildScrollView], which is a scrollable widget that has a single +/// child. +/// * [PageView], which is a scrolling list of child widgets that are each the +/// size of the viewport. +/// * [GridView], which is scrollable, 2D array of widgets. +/// * [CustomScrollView], which is a scrollable widget that creates custom +/// scroll effects using slivers. class ListView extends BoxScrollView { + /// Creates a scrollable, linear array of widgets from an explicit [List]. + /// + /// This constructor is appropriate for list views with a small number of + /// children because constructing the [List] requires doing work for every + /// child that could possibly be displayed in the list view instead of just + /// those children that are actually visible. ListView({ Key key, Axis scrollDirection: Axis.vertical, @@ -218,6 +366,17 @@ class ListView extends BoxScrollView { padding: padding, ); + /// Creates a scrollable, linear array of widgets that are created on demand. + /// + /// This constructor is appropriate for list views with a large (or infinite) + /// number of children because the builder is called only for those children + /// that are actually visible. + /// + /// Providing a non-null [itemCount] improves the ability of the [ListView] to + /// estimate the maximum scroll extent. + /// + /// [itemBuilder] will be called only with indices greater than or equal to + /// zero and less than [itemCount]. ListView.builder({ Key key, Axis scrollDirection: Axis.vertical, @@ -241,6 +400,10 @@ class ListView extends BoxScrollView { padding: padding, ); + /// Creates a scrollable, linear array of widgets with a custom child model. + /// + /// For example, a custom child model can control the algorithm used to + /// estimate the size of children that are not actually visible. ListView.custom({ Key key, Axis scrollDirection: Axis.vertical, @@ -265,8 +428,21 @@ class ListView extends BoxScrollView { assert(childrenDelegate != null); } + /// If non-null, forces the children to have the given extent in the scroll + /// direction. + /// + /// Specifying an [itemExtent] is more efficient than letting the children + /// determine their own extent because the scrolling machinery can make use of + /// the foreknowledge of the children's extent to save work, for example when + /// the scroll position changes drastically. final double itemExtent; + /// A delegate that provides the children for the [ListView]. + /// + /// The [ListView.custom] constructor lets you specify this delegate + /// explicitly. The [ListView] and [ListView.builder] constructors create a + /// [childrenDelegate] that wraps the given [List] and [IndexedWidgetBuilder], + /// respectively. final SliverChildDelegate childrenDelegate; @override @@ -288,15 +464,39 @@ class ListView extends BoxScrollView { } } -/// A scrollable grid of boxes. -// TODO(ianh): More documentation here. +/// A scrollable, 2D array of widgets. +/// +/// The most commonly used grid layouts are [GridView.count], which creates a +/// layout with a fixed number of tiles in the cross axis, and +/// [GridView.extent], which creates a layout with tiles that have a maximum +/// cross-axis extent. A custom [SliverGridDelegate] can produce an aribtrary 2D +/// arrangement of children, including arrangements that are unaligned or +/// overlapping. +/// +/// To create a grid with a large (or infinite) number of children, use the +/// [GridView.custom] constructor with either a [SliverChildBuilderDelegate] or +/// a custom [SliverChildDelegate]. +/// +/// To create a linear array of children, use a [ListView]. /// /// See also: /// -/// * [SingleChildScrollView], when you need to make a single child scrollable. -/// * [ListView], for a scrollable list of boxes. -/// * [PageView], for a scrollable that works page by page. +/// * [SingleChildScrollView], which is a scrollable widget that has a single +/// child. +/// * [ListView], which is scrollable, linear list of widgets. +/// * [PageView], which is a scrolling list of child widgets that are each the +/// size of the viewport. +/// * [CustomScrollView], which is a scrollable widget that creates custom +/// scroll effects using slivers. +/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with +/// a fixed number of tiles in the cross axis. +/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with +/// tiles that have a maximum cross-axis extent. class GridView extends BoxScrollView { + /// Creates a scrollable, 2D array of widgets with a custom + /// [SliverGridDelegate]. + /// + /// The [gridDelegate] argument must not be null. GridView({ Key key, Axis scrollDirection: Axis.vertical, @@ -321,6 +521,13 @@ class GridView extends BoxScrollView { assert(gridDelegate != null); } + /// Creates a scrollable, 2D array of widgets with both a custom + /// [SliverGridDelegate] and a custom [SliverChildDelegate]. + /// + /// To use an [IndexedWidgetBuilder] callback to build children, use the + /// [SliverChildBuilderDelegate]. + /// + /// The [gridDelegate] and [childrenDelegate] arguments must not be null. GridView.custom({ Key key, Axis scrollDirection: Axis.vertical, @@ -346,6 +553,10 @@ class GridView extends BoxScrollView { assert(childrenDelegate != null); } + /// Creates a scrollable, 2D array of widgets with a fixed number of tiles in + /// the cross axis. + /// + /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate]. GridView.count({ Key key, Axis scrollDirection: Axis.vertical, @@ -377,6 +588,10 @@ class GridView extends BoxScrollView { padding: padding, ); + /// Creates a scrollable, 2D array of widgets with tiles that have a maximum + /// cross-axis extent. + /// + /// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate]. GridView.extent({ Key key, Axis scrollDirection: Axis.vertical, @@ -408,8 +623,18 @@ class GridView extends BoxScrollView { padding: padding, ); + /// A delegate that controls the layout of the children within the [GridView]. + /// + /// The [GridView] and [GridView.custom] constructors let you specify this + /// delegate explicitly. The other constructors create a [gridDelegate] + /// implicitly. final SliverGridDelegate gridDelegate; + /// A delegate that provides the children for the [GridView]. + /// + /// The [GridView.custom] constructor lets you specify this delegate + /// explicitly. The other constructors create a [childrenDelegate] that wraps + /// the given child list. final SliverChildDelegate childrenDelegate; @override