scroll_view.dart 48.7 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:math' as math;

Adam Barth's avatar
Adam Barth committed
7 8 9
import 'package:flutter/rendering.dart';

import 'basic.dart';
10
import 'framework.dart';
11
import 'media_query.dart';
12
import 'primary_scroll_controller.dart';
13
import 'scroll_controller.dart';
14
import 'scroll_physics.dart';
Adam Barth's avatar
Adam Barth committed
15 16
import 'scrollable.dart';
import 'sliver.dart';
17
import 'viewport.dart';
Adam Barth's avatar
Adam Barth committed
18

19 20 21 22 23 24 25 26 27 28 29 30 31
/// 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
32
/// the viewport and deferring to its subclass to create the slivers.
33
///
34 35 36
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
37 38 39 40 41 42 43 44 45 46
/// 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.
47
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
48
///    the scroll position without using a [ScrollController].
49
abstract class ScrollView extends StatelessWidget {
50 51 52
  /// Creates a widget that scrolls.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
Adam Barth's avatar
Adam Barth committed
53 54
  ScrollView({
    Key key,
55 56
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
57
    this.controller,
58
    bool primary,
59
    ScrollPhysics physics,
60
    this.shrinkWrap = false,
61
    this.cacheExtent,
62 63 64
  }) : assert(reverse != null),
       assert(shrinkWrap != null),
       assert(!(controller != null && primary == true),
65
           'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
66
           'You cannot both set primary to true and pass an explicit controller.'
67 68 69 70
       ),
       primary = primary ?? controller == null && scrollDirection == Axis.vertical,
       physics = physics ?? (primary == true || (primary == null && controller == null && scrollDirection == Axis.vertical) ? const AlwaysScrollableScrollPhysics() : null),
       super(key: key);
Adam Barth's avatar
Adam Barth committed
71

72 73 74
  /// The axis along which the scroll view scrolls.
  ///
  /// Defaults to [Axis.vertical].
Adam Barth's avatar
Adam Barth committed
75 76
  final Axis scrollDirection;

77
  /// Whether the scroll view scrolls in the reading direction.
78 79 80 81 82 83
  ///
  /// 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.
  ///
Adam Barth's avatar
Adam Barth committed
84
  /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view
85 86 87 88
  /// scrolls from top to bottom when [reverse] is false and from bottom to top
  /// when [reverse] is true.
  ///
  /// Defaults to false.
89 90
  final bool reverse;

91 92 93 94
  /// An object that can be used to control the position to which this scroll
  /// view is scrolled.
  ///
  /// Must be null if [primary] is true.
95 96 97 98 99 100 101 102
  ///
  /// A [ScrollController] serves several purposes. It can be used to control
  /// the initial scroll position (see [ScrollController.initialScrollOffset]).
  /// It can be used to control whether the scroll view should automatically
  /// save and restore its scroll position in the [PageStorage] (see
  /// [ScrollController.keepScrollOffset]). It can be used to read the current
  /// scroll position (see [ScrollController.offset]), or change it (see
  /// [ScrollController.animateTo]).
103 104
  final ScrollController controller;

105 106 107
  /// Whether this is the primary scroll view associated with the parent
  /// [PrimaryScrollController].
  ///
108 109 110 111 112
  /// When this is true, the scroll view is scrollable even if it does not have
  /// sufficient content to actually scroll. Otherwise, by default the user can
  /// only scroll the view if it has sufficient content. See [physics].
  ///
  /// On iOS, this also identifies the scroll view that will scroll to top in
113 114
  /// response to a tap in the status bar.
  ///
115 116
  /// Defaults to true when [scrollDirection] is [Axis.vertical] and
  /// [controller] is null.
117 118
  final bool primary;

119 120 121 122 123
  /// 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.
  ///
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  /// Defaults to matching platform conventions. Furthermore, if [primary] is
  /// false, then the user cannot scroll if there is insufficient content to
  /// scroll, while if [primary] is true, they can always attempt to scroll.
  ///
  /// To force the scroll view to always be scrollable even if there is
  /// insufficient content, as if [primary] was true but without necessarily
  /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics
  /// object, as in:
  ///
  /// ```dart
  ///   physics: const AlwaysScrollableScrollPhysics(),
  /// ```
  ///
  /// To force the scroll view to use the default platform conventions and not
  /// be scrollable if there is insufficient content, regardless of the value of
  /// [primary], provide an explicit [ScrollPhysics] object, as in:
  ///
  /// ```dart
  ///   physics: const ScrollPhysics(),
  /// ```
144 145 146 147 148 149 150 151 152
  ///
  /// The physics can be changed dynamically (by providing a new object in a
  /// subsequent build), but new physics will only take effect if the _class_ of
  /// the provided object changes. Merely constructing a new instance with a
  /// different configuration is insufficient to cause the physics to be
  /// reapplied. (This is because the final object used is generated
  /// dynamically, which can be relatively expensive, and it would be
  /// inefficient to speculatively create this object each frame to see if the
  /// physics should be updated.)
Adam Barth's avatar
Adam Barth committed
153 154
  final ScrollPhysics physics;

155 156 157 158 159 160 161 162 163 164 165 166 167 168
  /// 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.
169 170
  final bool shrinkWrap;

171 172 173
  /// {@macro flutter.rendering.viewport.cacheExtent}
  final double cacheExtent;

174 175 176 177 178
  /// Returns the [AxisDirection] in which the scroll view scrolls.
  ///
  /// Combines the [scrollDirection] with the [reverse] boolean to obtain the
  /// concrete [AxisDirection].
  ///
179
  /// If the [scrollDirection] is [Axis.horizontal], the ambient
180
  /// [Directionality] is also considered when selecting the concrete
181 182 183 184
  /// [AxisDirection]. For example, if the ambient [Directionality] is
  /// [TextDirection.rtl], then the non-reversed [AxisDirection] is
  /// [AxisDirection.left] and the reversed [AxisDirection] is
  /// [AxisDirection.right].
185 186
  @protected
  AxisDirection getDirection(BuildContext context) {
187
    return getAxisDirectionFromAxisReverseAndDirectionality(context, scrollDirection, reverse);
188 189
  }

190 191
  /// Build the list of widgets to place inside the viewport.
  ///
192 193
  /// Subclasses should override this method to build the slivers for the inside
  /// of the viewport.
194
  @protected
195
  List<Widget> buildSlivers(BuildContext context);
Adam Barth's avatar
Adam Barth committed
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  /// Build the viewport.
  ///
  /// Subclasses may override this method to change how the viewport is built.
  /// The default implementation uses a [ShrinkWrappingViewport] if [shrinkWrap]
  /// is true, and a regular [Viewport] otherwise.
  @protected
  Widget buildViewport(
    BuildContext context,
    ViewportOffset offset,
    AxisDirection axisDirection,
    List<Widget> slivers,
  ) {
    if (shrinkWrap) {
      return new ShrinkWrappingViewport(
        axisDirection: axisDirection,
        offset: offset,
        slivers: slivers,
      );
    }
    return new Viewport(
      axisDirection: axisDirection,
      offset: offset,
      slivers: slivers,
220
      cacheExtent: cacheExtent,
221 222 223
    );
  }

224 225
  @override
  Widget build(BuildContext context) {
226 227
    final List<Widget> slivers = buildSlivers(context);
    final AxisDirection axisDirection = getDirection(context);
228

229
    final ScrollController scrollController = primary
230 231
        ? PrimaryScrollController.of(context)
        : controller;
232
    final Scrollable scrollable = new Scrollable(
233
      axisDirection: axisDirection,
234
      controller: scrollController,
Adam Barth's avatar
Adam Barth committed
235
      physics: physics,
236
      viewportBuilder: (BuildContext context, ViewportOffset offset) {
237 238
        return buildViewport(context, offset, axisDirection, slivers);
      },
239
    );
240 241 242
    return primary && scrollController != null
      ? new PrimaryScrollController.none(child: scrollable)
      : scrollable;
243
  }
244 245

  @override
246 247 248 249 250 251 252 253
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new EnumProperty<Axis>('scrollDirection', scrollDirection));
    properties.add(new FlagProperty('reverse', value: reverse, ifTrue: 'reversed', showName: true));
    properties.add(new DiagnosticsProperty<ScrollController>('controller', controller, showName: false, defaultValue: null));
    properties.add(new FlagProperty('primary', value: primary, ifTrue: 'using primary controller', showName: true));
    properties.add(new DiagnosticsProperty<ScrollPhysics>('physics', physics, showName: false, defaultValue: null));
    properties.add(new FlagProperty('shrinkWrap', value: shrinkWrap, ifTrue: 'shrink-wrapping', showName: true));
254 255 256
  }
}

257 258 259 260 261 262 263 264
/// 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].
///
265 266
/// [Widget]s in these [slivers] must produce [RenderSliver] objects.
///
267 268 269
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
/// ## Sample code
///
/// This sample code shows a scroll view that contains a flexible pinned app
/// bar, a grid, and an infinite list.
///
/// ```dart
/// new CustomScrollView(
///   slivers: <Widget>[
///     const SliverAppBar(
///       pinned: true,
///       expandedHeight: 250.0,
///       flexibleSpace: const FlexibleSpaceBar(
///         title: const Text('Demo'),
///       ),
///     ),
///     new SliverGrid(
///       gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
///         maxCrossAxisExtent: 200.0,
///         mainAxisSpacing: 10.0,
///         crossAxisSpacing: 10.0,
///         childAspectRatio: 4.0,
///       ),
///       delegate: new SliverChildBuilderDelegate(
///         (BuildContext context, int index) {
///           return new Container(
295
///             alignment: Alignment.center,
296 297 298 299 300 301 302 303 304 305 306 307
///             color: Colors.teal[100 * (index % 9)],
///             child: new Text('grid item $index'),
///           );
///         },
///         childCount: 20,
///       ),
///     ),
///     new SliverFixedExtentList(
///       itemExtent: 50.0,
///       delegate: new SliverChildBuilderDelegate(
///         (BuildContext context, int index) {
///           return new Container(
308
///             alignment: Alignment.center,
309 310 311 312 313 314 315 316 317 318
///             color: Colors.lightBlue[100 * (index % 9)],
///             child: new Text('list item $index'),
///           );
///         },
///       ),
///     ),
///   ],
/// )
/// ```
///
319 320 321 322 323 324 325 326 327 328
/// 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.
329
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
330
///    the scroll position without using a [ScrollController].
Adam Barth's avatar
Adam Barth committed
331
class CustomScrollView extends ScrollView {
332 333 334
  /// Creates a [ScrollView] that creates custom scroll effects using slivers.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
Adam Barth's avatar
Adam Barth committed
335 336
  CustomScrollView({
    Key key,
337 338
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
339
    ScrollController controller,
340
    bool primary,
Adam Barth's avatar
Adam Barth committed
341
    ScrollPhysics physics,
342
    bool shrinkWrap = false,
343
    double cacheExtent,
344
    this.slivers = const <Widget>[],
Adam Barth's avatar
Adam Barth committed
345 346 347 348
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
349
    controller: controller,
350
    primary: primary,
Adam Barth's avatar
Adam Barth committed
351 352
    physics: physics,
    shrinkWrap: shrinkWrap,
353
    cacheExtent: cacheExtent,
Adam Barth's avatar
Adam Barth committed
354 355
  );

356
  /// The slivers to place inside the viewport.
Adam Barth's avatar
Adam Barth committed
357 358 359 360 361 362
  final List<Widget> slivers;

  @override
  List<Widget> buildSlivers(BuildContext context) => slivers;
}

363
/// A [ScrollView] that uses a single child layout model.
364 365 366 367 368 369 370
///
/// 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.
371
abstract class BoxScrollView extends ScrollView {
372 373 374
  /// Creates a [ScrollView] uses a single child layout model.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
375 376
  BoxScrollView({
    Key key,
377 378
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
379
    ScrollController controller,
380
    bool primary,
381
    ScrollPhysics physics,
382
    bool shrinkWrap = false,
383
    this.padding,
384
    double cacheExtent,
385 386 387 388
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
389
    controller: controller,
390
    primary: primary,
391 392
    physics: physics,
    shrinkWrap: shrinkWrap,
393
    cacheExtent: cacheExtent,
394 395
  );

396
  /// The amount of space by which to inset the children.
397
  final EdgeInsetsGeometry padding;
398 399 400 401

  @override
  List<Widget> buildSlivers(BuildContext context) {
    Widget sliver = buildChildLayout(context);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    EdgeInsetsGeometry effectivePadding = padding;
    if (padding == null) {
      final MediaQueryData mediaQuery = MediaQuery.of(context, nullOk: true);
      if (mediaQuery != null) {
        // Automatically pad sliver with padding from MediaQuery.
        final EdgeInsets mediaQueryHorizontalPadding =
            mediaQuery.padding.copyWith(top: 0.0, bottom: 0.0);
        final EdgeInsets mediaQueryVerticalPadding =
            mediaQuery.padding.copyWith(left: 0.0, right: 0.0);
        // Consume the main axis padding with SliverPadding.
        effectivePadding = scrollDirection == Axis.vertical
            ? mediaQueryVerticalPadding
            : mediaQueryHorizontalPadding;
        // Leave behind the cross axis padding.
        sliver = new MediaQuery(
          data: mediaQuery.copyWith(
            padding: scrollDirection == Axis.vertical
                ? mediaQueryHorizontalPadding
                : mediaQueryVerticalPadding,
          ),
          child: sliver,
        );
      }
    }

    if (effectivePadding != null)
      sliver = new SliverPadding(padding: effectivePadding, sliver: sliver);
429 430 431
    return <Widget>[ sliver ];
  }

432
  /// Subclasses should override this method to build the layout model.
433 434 435 436
  @protected
  Widget buildChildLayout(BuildContext context);

  @override
437 438 439
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
440 441 442
  }
}

443
/// A scrollable list of widgets arranged linearly.
444 445 446 447 448 449 450 451 452 453 454
///
/// [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.
///
455
/// There are four options for constructing a [ListView]:
456
///
457
///  1. The default constructor takes an explicit [List<Widget>] of children. This
458 459 460 461 462
///     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.
///
463 464 465
///  2. The [ListView.builder] constructor 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
466 467
///     only for those children that are actually visible.
///
468 469 470 471 472 473 474
///  3. The [ListView.separated] constructor takes two [IndexedWidgetBuilder]s:
///     `itemBuilder` builds child items on demand, and `separatorBuilder`
///     similarly builds separator children which appear in between the child items.
///     This constructor is appropriate for list views with a fixed number of children.
///
///  4. The [ListView.custom] constructor takes a [SliverChildDelegate], which provides
///     the ability to customize additional aspects of the child model. For example,
475 476
///     a [SliverChildDelegate] can control the algorithm used to estimate the
///     size of children that are not actually visible.
477
///
478 479 480
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
481 482 483 484
/// By default, [ListView] will automatically pad the list's scrollable
/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
/// padding. To avoid this behavior, override with a zero [padding] property.
///
485 486 487 488 489 490 491 492 493 494 495 496 497 498
/// ## Sample code
///
/// An infinite list of children:
///
/// ```dart
/// new ListView.builder(
///   padding: new EdgeInsets.all(8.0),
///   itemExtent: 20.0,
///   itemBuilder: (BuildContext context, int index) {
///     return new Text('entry $index');
///   },
/// )
/// ```
///
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
/// ## Child elements' lifecycle
///
/// ### Creation
///
/// While laying out the list, visible children's elements, states and render
/// objects will be created lazily based on existing widgets (such as when using
/// the default constructor) or lazily provided ones (such as when using the
/// [ListView.builder] constructor).
///
/// ### Destruction
///
/// When a child is scrolled out of view, the associated element subtree,
/// states and render objects are destroyed. A new child at the same position
/// in the list will be lazily recreated along with new elements, states and
/// render objects when it is scrolled back.
///
/// ### Destruction mitigation
///
/// In order to preserve state as child elements are scrolled in and out of
/// view, the following options are possible:
///
///  * Moving the ownership of non-trivial UI-state-driving business logic
///    out of the list child subtree. For instance, if a list contains posts
///    with their number of upvotes coming from a cached network response, store
///    the list of posts and upvote number in a data model outside the list. Let
///    the list child UI subtree be easily recreate-able from the
///    source-of-truth model object. Use [StatefulWidget]s in the child
///    widget subtree to store instantaneous UI state only.
///
///  * Letting [KeepAlive] be the root widget of the list child widget subtree
///    that needs to be preserved. The [KeepAlive] widget marks the child
///    subtree's top render object child for keep-alive. When the associated top
///    render object is scrolled out of view, the list keeps the child's render
///    object (and by extension, its associated elements and states) in a cache
///    list instead of destroying them. When scrolled back into view, the render
///    object is repainted as-is (if it wasn't marked dirty in the interim).
///
///    This only works if [addAutomaticKeepAlives] and [addRepaintBoundaries]
///    are false since those parameters cause the [ListView] to wrap each child
///    widget subtree with other widgets.
///
///  * Using [AutomaticKeepAlive] widgets (inserted by default when
///    [addAutomaticKeepAlives] is true). Instead of unconditionally caching the
///    child element subtree when scrolling off-screen like [KeepAlive],
///    [AutomaticKeepAlive] can let whether to cache the subtree be determined
///    by descendant logic in the subtree.
///
///    As an example, the [EditableText] widget signals its list child element
///    subtree to stay alive while its text field has input focus. If it doesn't
///    have focus and no other descendants signaled for keep-alive via a
///    [KeepAliveNotification], the list child element subtree will be destroyed
///    when scrolled away.
///
///    [AutomaticKeepAlive] descendants typically signal it to be kept alive
///    by using the [AutomaticKeepAliveClientMixin], then implementing the
///    [wantKeepAlive] getter and calling [updateKeepAlive].
///
556 557 558
/// ## Transitioning to [CustomScrollView]
///
/// A [ListView] is basically a [CustomScrollView] with a single [SliverList] in
559
/// its [CustomScrollView.slivers] property.
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
///
/// If [ListView] is no longer sufficient, for example because the scroll view
/// is to have both a list and a grid, or because the list is to be combined
/// with a [SliverAppBar], etc, it is straight-forward to port code from using
/// [ListView] to using [CustomScrollView] directly.
///
/// The [key], [scrollDirection], [reverse], [controller], [primary], [physics],
/// and [shrinkWrap] properties on [ListView] map directly to the identically
/// named properties on [CustomScrollView].
///
/// The [CustomScrollView.slivers] property should be a list containing either a
/// [SliverList] or a [SliverFixedExtentList]; the former if [itemExtent] on the
/// [ListView] was null, and the latter if [itemExtent] was not null.
///
/// The [childrenDelegate] property on [ListView] corresponds to the
/// [SliverList.delegate] (or [SliverFixedExtentList.delegate]) property. The
/// [new ListView] constructor's `children` argument corresponds to the
/// [childrenDelegate] being a [SliverChildListDelegate] with that same
/// argument. The [new ListView.builder] constructor's `itemBuilder` and
/// `childCount` arguments correspond to the [childrenDelegate] being a
/// [SliverChildBuilderDelegate] with the matching arguments.
///
/// The [padding] property corresponds to having a [SliverPadding] in the
/// [CustomScrollView.slivers] property instead of the list itself, and having
/// the [SliverList] instead be a child of the [SliverPadding].
///
586 587 588
/// [CustomScrollView]s don't automatically avoid obstructions from [MediaQuery]
/// like [ListView]s do. To reproduce the behavior, wrap the slivers in
/// [SliverSafeArea]s.
589
///
590 591 592 593
/// Once code has been ported to use [CustomScrollView], other slivers, such as
/// [SliverGrid] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
/// list.
///
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
/// ### Sample code
///
/// Here are two brief snippets showing a [ListView] and its equivalent using
/// [CustomScrollView]:
///
/// ```dart
/// new ListView(
///   shrinkWrap: true,
///   padding: const EdgeInsets.all(20.0),
///   children: <Widget>[
///     const Text('I\'m dedicating every day to you'),
///     const Text('Domestic life was never quite my style'),
///     const Text('When you smile, you knock me out, I fall apart'),
///     const Text('And I thought I was so smart'),
///   ],
/// )
/// ```
///
/// ```dart
/// new CustomScrollView(
///   shrinkWrap: true,
///   slivers: <Widget>[
///     new SliverPadding(
///       padding: const EdgeInsets.all(20.0),
///       sliver: new SliverList(
///         delegate: new SliverChildListDelegate(
///           <Widget>[
///             const Text('I\'m dedicating every day to you'),
///             const Text('Domestic life was never quite my style'),
///             const Text('When you smile, you knock me out, I fall apart'),
///             const Text('And I thought I was so smart'),
///           ],
///         ),
///       ),
///     ),
///   ],
/// )
/// ```
///
633 634
/// See also:
///
635 636 637 638 639 640 641
///  * [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.
642 643
///  * [ListBody], which arranges its children in a similar manner, but without
///    scrolling.
644
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
645
///    the scroll position without using a [ScrollController].
646
class ListView extends BoxScrollView {
647 648 649 650 651 652
  /// 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.
653 654 655
  ///
  /// It is usually more efficient to create children on demand using [new
  /// ListView.builder].
656
  ///
657 658 659 660
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
661
  /// null.
662 663
  ListView({
    Key key,
664 665
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
666
    ScrollController controller,
667
    bool primary,
668
    ScrollPhysics physics,
669
    bool shrinkWrap = false,
670
    EdgeInsetsGeometry padding,
671
    this.itemExtent,
672 673
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
674
    double cacheExtent,
675
    List<Widget> children = const <Widget>[],
676 677
  }) : childrenDelegate = new SliverChildListDelegate(
         children,
678
         addAutomaticKeepAlives: addAutomaticKeepAlives,
679 680
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
681 682 683
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
684
    controller: controller,
685
    primary: primary,
686 687 688
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
689
    cacheExtent: cacheExtent,
690 691
  );

692 693 694 695 696 697
  /// 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.
  ///
698
  /// Providing a non-null `itemCount` improves the ability of the [ListView] to
699 700
  /// estimate the maximum scroll extent.
  ///
701 702 703 704 705 706 707 708 709
  /// The `itemBuilder` callback will be called only with indices greater than
  /// or equal to zero and less than `itemCount`.
  ///
  /// The `itemBuilder` should actually create the widget instances when called.
  /// Avoid using a builder that returns a previously-constructed widget; if the
  /// list view's children are created in advance, or all at once when the
  /// [ListView] itself is created, it is more efficient to use [new ListView].
  /// Even more efficient, however, is to create the instances on demand using
  /// this constructor's `itemBuilder` callback.
710
  ///
711 712 713 714 715
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
  /// be null.
716 717
  ListView.builder({
    Key key,
718 719
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
720
    ScrollController controller,
721
    bool primary,
722
    ScrollPhysics physics,
723
    bool shrinkWrap = false,
724
    EdgeInsetsGeometry padding,
725
    this.itemExtent,
726
    @required IndexedWidgetBuilder itemBuilder,
727
    int itemCount,
728 729
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
730
    double cacheExtent,
731 732 733
  }) : childrenDelegate = new SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
734
         addAutomaticKeepAlives: addAutomaticKeepAlives,
735 736
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
737 738 739
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
740
    controller: controller,
741
    primary: primary,
742 743
    physics: physics,
    shrinkWrap: shrinkWrap,
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
    padding: padding,
    cacheExtent: cacheExtent
  );

  /// Creates a fixed-length scrollable linear array of list "items" separated
  /// by list item "separators".
  ///
  /// This constructor is appropriate for list views with a large number of
  /// item and separator children because the builders are only called for
  /// the children that are actually visible.
  ///
  /// The `itemBuilder` callback will be called with indices greater than
  /// or equal to zero and less than `itemCount`.
  ///
  /// Separators only appear between list items: separator 0 appears after item
  /// 0 and the last separator appears before the last item.
  ///
  /// The `separatorBuilder` callback will be called with indices greater than
  /// or equal to zero and less than `itemCount - 1`.
  ///
  /// The `itemBuilder` and `separatorBuilder` callbacks should actually create
  /// widget instances when called. Avoid using a builder that returns a
  /// previously-constructed widget; if the list view's children are created in
  /// advance, or all at once when the [ListView] itself is created, it is more
  /// efficient to use [new ListView].
  ///
  /// ## Sample code
  ///
  /// This example shows how to create [ListView] whose [ListTile] list items
  /// are separated by [Divider]s.
  ///
  /// ```dart
  /// new ListView.separated(
  ///   itemCount: 25,
  ///   separatorBuilder: (BuildContext context, int index) => new Divider(),
  ///   itemBuilder: (BuildContext context, int index) {
  ///     return new ListTile(
  ///       title: new Text('item $index'),
  ///     );
  ///   },
  /// )
  /// ```
  ///
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
  /// be null.
  ListView.separated({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    @required IndexedWidgetBuilder itemBuilder,
    @required IndexedWidgetBuilder separatorBuilder,
    @required int itemCount,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    double cacheExtent,
  }) : assert(itemBuilder != null),
       assert(separatorBuilder != null),
       assert(itemCount != null && itemCount >= 0),
       itemExtent = null,
       childrenDelegate = new SliverChildBuilderDelegate(
         (BuildContext context, int index) {
           final int itemIndex = index ~/ 2;
814
           return index.isEven
815 816 817 818 819 820 821 822 823 824 825 826 827 828
             ? itemBuilder(context, itemIndex)
             : separatorBuilder(context, itemIndex);
         },
         childCount: math.max(0, itemCount * 2 - 1),
         addAutomaticKeepAlives: addAutomaticKeepAlives,
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
    controller: controller,
    primary: primary,
    physics: physics,
    shrinkWrap: shrinkWrap,
829
    padding: padding,
830
    cacheExtent: cacheExtent
831 832
  );

833 834 835 836
  /// 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.
837 838
  ListView.custom({
    Key key,
839 840
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
841
    ScrollController controller,
842
    bool primary,
843
    ScrollPhysics physics,
844
    bool shrinkWrap = false,
845
    EdgeInsetsGeometry padding,
846 847
    this.itemExtent,
    @required this.childrenDelegate,
848
    double cacheExtent,
849 850 851 852 853 854 855 856 857 858
  }) : assert(childrenDelegate != null),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
859
        cacheExtent: cacheExtent,
860
       );
861

862 863 864 865 866 867 868
  /// 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.
869 870
  final double itemExtent;

871 872 873 874 875 876
  /// 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.
877
  final SliverChildDelegate childrenDelegate;
878 879 880 881

  @override
  Widget buildChildLayout(BuildContext context) {
    if (itemExtent != null) {
882
      return new SliverFixedExtentList(
883 884 885 886
        delegate: childrenDelegate,
        itemExtent: itemExtent,
      );
    }
887
    return new SliverList(delegate: childrenDelegate);
888 889 890
  }

  @override
891 892 893
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DoubleProperty('itemExtent', itemExtent, defaultValue: null));
894
  }
895 896
}

897 898
/// A scrollable, 2D array of widgets.
///
899 900 901
/// The main axis direction of a grid is the direction in which it scrolls (the
/// [scrollDirection]).
///
902 903 904
/// 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
905
/// cross-axis extent. A custom [SliverGridDelegate] can produce an arbitrary 2D
906 907 908 909
/// arrangement of children, including arrangements that are unaligned or
/// overlapping.
///
/// To create a grid with a large (or infinite) number of children, use the
910 911 912 913 914
/// [GridView.builder] constructor with either a
/// [SliverGridDelegateWithFixedCrossAxisCount] or a
/// [SliverGridDelegateWithMaxCrossAxisExtent] for the [gridDelegate].
///
/// To use a custom [SliverChildDelegate], use [GridView.custom].
915 916
///
/// To create a linear array of children, use a [ListView].
917
///
918 919 920
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
921 922 923
/// ## Transitioning to [CustomScrollView]
///
/// A [GridView] is basically a [CustomScrollView] with a single [SliverGrid] in
924
/// its [CustomScrollView.slivers] property.
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
///
/// If [GridView] is no longer sufficient, for example because the scroll view
/// is to have both a grid and a list, or because the grid is to be combined
/// with a [SliverAppBar], etc, it is straight-forward to port code from using
/// [GridView] to using [CustomScrollView] directly.
///
/// The [key], [scrollDirection], [reverse], [controller], [primary], [physics],
/// and [shrinkWrap] properties on [GridView] map directly to the identically
/// named properties on [CustomScrollView].
///
/// The [CustomScrollView.slivers] property should be a list containing just a
/// [SliverGrid].
///
/// The [childrenDelegate] property on [GridView] corresponds to the
/// [SliverGrid.delegate] property, and the [gridDelegate] property on the
/// [GridView] corresponds to the [SliverGrid.gridDelegate] property.
///
/// The [new GridView], [new GridView.count], and [new GridView.extent]
/// constructors' `children` arguments correspond to the [childrenDelegate]
/// being a [SliverChildListDelegate] with that same argument. The [new
/// GridView.builder] constructor's `itemBuilder` and `childCount` arguments
/// correspond to the [childrenDelegate] being a [SliverChildBuilderDelegate]
/// with the matching arguments.
///
/// The [new GridView.count] and [new GridView.extent] constructors create
/// custom grid delegates, and have equivalently named constructors on
/// [SliverGrid] to ease the transition: [new SliverGrid.count] and [new
/// SliverGrid.extent] respectively.
///
/// The [padding] property corresponds to having a [SliverPadding] in the
/// [CustomScrollView.slivers] property instead of the grid itself, and having
/// the [SliverGrid] instead be a child of the [SliverPadding].
///
958 959 960 961
/// By default, [ListView] will automatically pad the list's scrollable
/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
/// padding. To avoid this behavior, override with a zero [padding] property.
///
962
/// Once code has been ported to use [CustomScrollView], other slivers, such as
963
/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
964 965
/// list.
///
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
/// ### Sample code
///
/// Here are two brief snippets showing a [GridView] and its equivalent using
/// [CustomScrollView]:
///
/// ```dart
/// new GridView.count(
///   primary: false,
///   padding: const EdgeInsets.all(20.0),
///   crossAxisSpacing: 10.0,
///   crossAxisCount: 2,
///   children: <Widget>[
///     const Text('He\'d have you all unravel at the'),
///     const Text('Heed not the rabble'),
///     const Text('Sound of screams but the'),
///     const Text('Who scream'),
///     const Text('Revolution is coming...'),
///     const Text('Revolution, they...'),
///   ],
/// )
/// ```
///
/// ```dart
/// new CustomScrollView(
///   primary: false,
///   slivers: <Widget>[
///     new SliverPadding(
///       padding: const EdgeInsets.all(20.0),
///       sliver: new SliverGrid.count(
///         crossAxisSpacing: 10.0,
///         crossAxisCount: 2,
///         children: <Widget>[
///           const Text('He\'d have you all unravel at the'),
///           const Text('Heed not the rabble'),
///           const Text('Sound of screams but the'),
///           const Text('Who scream'),
///           const Text('Revolution is coming...'),
///           const Text('Revolution, they...'),
///         ],
///       ),
///     ),
///   ],
/// )
/// ```
///
1011 1012
/// See also:
///
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
///  * [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.
1024
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
1025
///    the scroll position without using a [ScrollController].
1026
class GridView extends BoxScrollView {
1027 1028 1029 1030
  /// Creates a scrollable, 2D array of widgets with a custom
  /// [SliverGridDelegate].
  ///
  /// The [gridDelegate] argument must not be null.
1031
  ///
1032 1033 1034 1035
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
1036
  /// null.
1037
  GridView({
1038
    Key key,
1039 1040
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1041
    ScrollController controller,
1042
    bool primary,
1043
    ScrollPhysics physics,
1044
    bool shrinkWrap = false,
1045
    EdgeInsetsGeometry padding,
1046
    @required this.gridDelegate,
1047 1048
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
1049
    double cacheExtent,
1050
    List<Widget> children = const <Widget>[],
1051
  }) : assert(gridDelegate != null),
1052 1053
       childrenDelegate = new SliverChildListDelegate(
         children,
1054
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1055 1056
         addRepaintBoundaries: addRepaintBoundaries,
       ),
1057 1058 1059 1060 1061 1062 1063 1064 1065
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1066
        cacheExtent: cacheExtent,
1067
       );
1068

1069 1070 1071 1072 1073 1074
  /// Creates a scrollable, 2D array of widgets that are created on demand.
  ///
  /// This constructor is appropriate for grid views with a large (or infinite)
  /// number of children because the builder is called only for those children
  /// that are actually visible.
  ///
1075
  /// Providing a non-null `itemCount` improves the ability of the [GridView] to
1076 1077
  /// estimate the maximum scroll extent.
  ///
1078 1079
  /// `itemBuilder` will be called only with indices greater than or equal to
  /// zero and less than `itemCount`.
1080 1081
  ///
  /// The [gridDelegate] argument must not be null.
1082
  ///
1083 1084 1085 1086 1087
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
  /// be null.
1088 1089
  GridView.builder({
    Key key,
1090 1091
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1092 1093 1094
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
1095
    bool shrinkWrap = false,
1096
    EdgeInsetsGeometry padding,
1097 1098 1099
    @required this.gridDelegate,
    @required IndexedWidgetBuilder itemBuilder,
    int itemCount,
1100 1101
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
1102
    double cacheExtent,
1103
  }) : assert(gridDelegate != null),
1104 1105 1106
       childrenDelegate = new SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
1107
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1108 1109
         addRepaintBoundaries: addRepaintBoundaries,
       ),
1110 1111 1112 1113 1114 1115 1116 1117 1118
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1119
        cacheExtent: cacheExtent,
1120
       );
1121

1122 1123 1124
  /// Creates a scrollable, 2D array of widgets with both a custom
  /// [SliverGridDelegate] and a custom [SliverChildDelegate].
  ///
1125 1126
  /// To use an [IndexedWidgetBuilder] callback to build children, either use
  /// a [SliverChildBuilderDelegate] or use the [GridView.builder] constructor.
1127 1128
  ///
  /// The [gridDelegate] and [childrenDelegate] arguments must not be null.
1129
  GridView.custom({
1130
    Key key,
1131 1132
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1133
    ScrollController controller,
1134
    bool primary,
1135
    ScrollPhysics physics,
1136
    bool shrinkWrap = false,
1137
    EdgeInsetsGeometry padding,
1138 1139
    @required this.gridDelegate,
    @required this.childrenDelegate,
1140
    double cacheExtent,
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
  }) : assert(gridDelegate != null),
       assert(childrenDelegate != null),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1152
        cacheExtent: cacheExtent,
1153
       );
1154

1155 1156 1157 1158
  /// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
  /// the cross axis.
  ///
  /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
1159
  ///
1160 1161 1162 1163
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
1164 1165
  /// null.
  ///
1166 1167 1168
  /// See also:
  ///
  ///  * [new SliverGrid.count], the equivalent constructor for [SliverGrid].
1169 1170
  GridView.count({
    Key key,
1171 1172
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1173
    ScrollController controller,
1174
    bool primary,
1175
    ScrollPhysics physics,
1176
    bool shrinkWrap = false,
1177
    EdgeInsetsGeometry padding,
1178
    @required int crossAxisCount,
1179 1180 1181 1182 1183
    double mainAxisSpacing = 0.0,
    double crossAxisSpacing = 0.0,
    double childAspectRatio = 1.0,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
1184
    double cacheExtent,
1185
    List<Widget> children = const <Widget>[],
1186
  }) : gridDelegate = new SliverGridDelegateWithFixedCrossAxisCount(
1187 1188 1189 1190 1191
         crossAxisCount: crossAxisCount,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),
1192 1193
       childrenDelegate = new SliverChildListDelegate(
         children,
1194
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1195 1196
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
1197 1198 1199
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
1200
    controller: controller,
1201
    primary: primary,
1202 1203 1204
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
1205
    cacheExtent: cacheExtent,
1206
  );
1207

1208 1209
  /// Creates a scrollable, 2D array of widgets with tiles that each have a
  /// maximum cross-axis extent.
1210 1211
  ///
  /// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
1212
  ///
1213 1214 1215 1216
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
1217 1218
  /// null.
  ///
1219 1220 1221
  /// See also:
  ///
  ///  * [new SliverGrid.extent], the equivalent constructor for [SliverGrid].
1222
  GridView.extent({
1223
    Key key,
1224 1225
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1226
    ScrollController controller,
1227
    bool primary,
1228
    ScrollPhysics physics,
1229
    bool shrinkWrap = false,
1230
    EdgeInsetsGeometry padding,
1231
    @required double maxCrossAxisExtent,
1232 1233 1234 1235 1236 1237
    double mainAxisSpacing = 0.0,
    double crossAxisSpacing = 0.0,
    double childAspectRatio = 1.0,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    List<Widget> children = const <Widget>[],
1238
  }) : gridDelegate = new SliverGridDelegateWithMaxCrossAxisExtent(
1239 1240 1241 1242 1243
         maxCrossAxisExtent: maxCrossAxisExtent,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),
1244 1245
       childrenDelegate = new SliverChildListDelegate(
         children,
1246
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1247 1248
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
1249 1250 1251
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
1252
    controller: controller,
1253
    primary: primary,
1254 1255 1256 1257
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  );
1258

1259 1260 1261 1262 1263
  /// 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.
1264 1265
  final SliverGridDelegate gridDelegate;

1266 1267 1268 1269 1270
  /// 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.
1271
  final SliverChildDelegate childrenDelegate;
1272

1273
  @override
1274 1275 1276
  Widget buildChildLayout(BuildContext context) {
    return new SliverGrid(
      delegate: childrenDelegate,
1277 1278
      gridDelegate: gridDelegate,
    );
Adam Barth's avatar
Adam Barth committed
1279 1280
  }
}