scroll_view.dart 45.8 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
/// ## Transitioning to [CustomScrollView]
///
/// A [ListView] is basically a [CustomScrollView] with a single [SliverList] in
502
/// its [CustomScrollView.slivers] property.
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
///
/// 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].
///
529 530 531
/// [CustomScrollView]s don't automatically avoid obstructions from [MediaQuery]
/// like [ListView]s do. To reproduce the behavior, wrap the slivers in
/// [SliverSafeArea]s.
532
///
533 534 535 536
/// Once code has been ported to use [CustomScrollView], other slivers, such as
/// [SliverGrid] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
/// list.
///
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
/// ### 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'),
///           ],
///         ),
///       ),
///     ),
///   ],
/// )
/// ```
///
576 577
/// See also:
///
578 579 580 581 582 583 584
///  * [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.
585 586
///  * [ListBody], which arranges its children in a similar manner, but without
///    scrolling.
587
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
588
///    the scroll position without using a [ScrollController].
589
class ListView extends BoxScrollView {
590 591 592 593 594 595
  /// 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.
596 597 598
  ///
  /// It is usually more efficient to create children on demand using [new
  /// ListView.builder].
599
  ///
600 601 602 603
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
604
  /// null.
605 606
  ListView({
    Key key,
607 608
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
609
    ScrollController controller,
610
    bool primary,
611
    ScrollPhysics physics,
612
    bool shrinkWrap = false,
613
    EdgeInsetsGeometry padding,
614
    this.itemExtent,
615 616
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
617
    double cacheExtent,
618
    List<Widget> children = const <Widget>[],
619 620
  }) : childrenDelegate = new SliverChildListDelegate(
         children,
621
         addAutomaticKeepAlives: addAutomaticKeepAlives,
622 623
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
624 625 626
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
627
    controller: controller,
628
    primary: primary,
629 630 631
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
632
    cacheExtent: cacheExtent,
633 634
  );

635 636 637 638 639 640
  /// 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.
  ///
641
  /// Providing a non-null `itemCount` improves the ability of the [ListView] to
642 643
  /// estimate the maximum scroll extent.
  ///
644 645 646 647 648 649 650 651 652
  /// 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.
653
  ///
654 655 656 657 658
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
  /// be null.
659 660
  ListView.builder({
    Key key,
661 662
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
663
    ScrollController controller,
664
    bool primary,
665
    ScrollPhysics physics,
666
    bool shrinkWrap = false,
667
    EdgeInsetsGeometry padding,
668
    this.itemExtent,
669
    @required IndexedWidgetBuilder itemBuilder,
670
    int itemCount,
671 672
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
673
    double cacheExtent,
674 675 676
  }) : childrenDelegate = new SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
677
         addAutomaticKeepAlives: addAutomaticKeepAlives,
678 679
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
680 681 682
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
683
    controller: controller,
684
    primary: primary,
685 686
    physics: physics,
    shrinkWrap: shrinkWrap,
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
    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;
757
           return index.isEven
758 759 760 761 762 763 764 765 766 767 768 769 770 771
             ? 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,
772
    padding: padding,
773
    cacheExtent: cacheExtent
774 775
  );

776 777 778 779
  /// 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.
780 781
  ListView.custom({
    Key key,
782 783
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
784
    ScrollController controller,
785
    bool primary,
786
    ScrollPhysics physics,
787
    bool shrinkWrap = false,
788
    EdgeInsetsGeometry padding,
789 790
    this.itemExtent,
    @required this.childrenDelegate,
791
    double cacheExtent,
792 793 794 795 796 797 798 799 800 801
  }) : assert(childrenDelegate != null),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
802
        cacheExtent: cacheExtent,
803
       );
804

805 806 807 808 809 810 811
  /// 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.
812 813
  final double itemExtent;

814 815 816 817 818 819
  /// 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.
820
  final SliverChildDelegate childrenDelegate;
821 822 823 824

  @override
  Widget buildChildLayout(BuildContext context) {
    if (itemExtent != null) {
825
      return new SliverFixedExtentList(
826 827 828 829
        delegate: childrenDelegate,
        itemExtent: itemExtent,
      );
    }
830
    return new SliverList(delegate: childrenDelegate);
831 832 833
  }

  @override
834 835 836
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DoubleProperty('itemExtent', itemExtent, defaultValue: null));
837
  }
838 839
}

840 841
/// A scrollable, 2D array of widgets.
///
842 843 844
/// The main axis direction of a grid is the direction in which it scrolls (the
/// [scrollDirection]).
///
845 846 847
/// 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
848
/// cross-axis extent. A custom [SliverGridDelegate] can produce an arbitrary 2D
849 850 851 852
/// arrangement of children, including arrangements that are unaligned or
/// overlapping.
///
/// To create a grid with a large (or infinite) number of children, use the
853 854 855 856 857
/// [GridView.builder] constructor with either a
/// [SliverGridDelegateWithFixedCrossAxisCount] or a
/// [SliverGridDelegateWithMaxCrossAxisExtent] for the [gridDelegate].
///
/// To use a custom [SliverChildDelegate], use [GridView.custom].
858 859
///
/// To create a linear array of children, use a [ListView].
860
///
861 862 863
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
864 865 866
/// ## Transitioning to [CustomScrollView]
///
/// A [GridView] is basically a [CustomScrollView] with a single [SliverGrid] in
867
/// its [CustomScrollView.slivers] property.
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
///
/// 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].
///
901 902 903 904
/// 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.
///
905
/// Once code has been ported to use [CustomScrollView], other slivers, such as
906
/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
907 908
/// list.
///
909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 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
/// ### 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...'),
///         ],
///       ),
///     ),
///   ],
/// )
/// ```
///
954 955
/// See also:
///
956 957 958 959 960 961 962 963 964 965 966
///  * [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.
967
///  * [ScrollNotification] and [NotificationListener], which can be used to watch
968
///    the scroll position without using a [ScrollController].
969
class GridView extends BoxScrollView {
970 971 972 973
  /// Creates a scrollable, 2D array of widgets with a custom
  /// [SliverGridDelegate].
  ///
  /// The [gridDelegate] argument must not be null.
974
  ///
975 976 977 978
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
979
  /// null.
980
  GridView({
981
    Key key,
982 983
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
984
    ScrollController controller,
985
    bool primary,
986
    ScrollPhysics physics,
987
    bool shrinkWrap = false,
988
    EdgeInsetsGeometry padding,
989
    @required this.gridDelegate,
990 991
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
992
    double cacheExtent,
993
    List<Widget> children = const <Widget>[],
994
  }) : assert(gridDelegate != null),
995 996
       childrenDelegate = new SliverChildListDelegate(
         children,
997
         addAutomaticKeepAlives: addAutomaticKeepAlives,
998 999
         addRepaintBoundaries: addRepaintBoundaries,
       ),
1000 1001 1002 1003 1004 1005 1006 1007 1008
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1009
        cacheExtent: cacheExtent,
1010
       );
1011

1012 1013 1014 1015 1016 1017
  /// 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.
  ///
1018
  /// Providing a non-null `itemCount` improves the ability of the [GridView] to
1019 1020
  /// estimate the maximum scroll extent.
  ///
1021 1022
  /// `itemBuilder` will be called only with indices greater than or equal to
  /// zero and less than `itemCount`.
1023 1024
  ///
  /// The [gridDelegate] argument must not be null.
1025
  ///
1026 1027 1028 1029 1030
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
  /// be null.
1031 1032
  GridView.builder({
    Key key,
1033 1034
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1035 1036 1037
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
1038
    bool shrinkWrap = false,
1039
    EdgeInsetsGeometry padding,
1040 1041 1042
    @required this.gridDelegate,
    @required IndexedWidgetBuilder itemBuilder,
    int itemCount,
1043 1044
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
1045
    double cacheExtent,
1046
  }) : assert(gridDelegate != null),
1047 1048 1049
       childrenDelegate = new SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
1050
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1051 1052
         addRepaintBoundaries: addRepaintBoundaries,
       ),
1053 1054 1055 1056 1057 1058 1059 1060 1061
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1062
        cacheExtent: cacheExtent,
1063
       );
1064

1065 1066 1067
  /// Creates a scrollable, 2D array of widgets with both a custom
  /// [SliverGridDelegate] and a custom [SliverChildDelegate].
  ///
1068 1069
  /// To use an [IndexedWidgetBuilder] callback to build children, either use
  /// a [SliverChildBuilderDelegate] or use the [GridView.builder] constructor.
1070 1071
  ///
  /// The [gridDelegate] and [childrenDelegate] arguments must not be null.
1072
  GridView.custom({
1073
    Key key,
1074 1075
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1076
    ScrollController controller,
1077
    bool primary,
1078
    ScrollPhysics physics,
1079
    bool shrinkWrap = false,
1080
    EdgeInsetsGeometry padding,
1081 1082
    @required this.gridDelegate,
    @required this.childrenDelegate,
1083
    double cacheExtent,
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
  }) : assert(gridDelegate != null),
       assert(childrenDelegate != null),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
1095
        cacheExtent: cacheExtent,
1096
       );
1097

1098 1099 1100 1101
  /// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
  /// the cross axis.
  ///
  /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
1102
  ///
1103 1104 1105 1106
  /// The `addAutomaticKeepAlives` argument corresponds to the
  /// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
  /// `addRepaintBoundaries` argument corresponds to the
  /// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
1107 1108
  /// null.
  ///
1109 1110 1111
  /// See also:
  ///
  ///  * [new SliverGrid.count], the equivalent constructor for [SliverGrid].
1112 1113
  GridView.count({
    Key key,
1114 1115
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
1116
    ScrollController controller,
1117
    bool primary,
1118
    ScrollPhysics physics,
1119
    bool shrinkWrap = false,
1120
    EdgeInsetsGeometry padding,
1121
    @required int crossAxisCount,
1122 1123 1124 1125 1126
    double mainAxisSpacing = 0.0,
    double crossAxisSpacing = 0.0,
    double childAspectRatio = 1.0,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
1127
    double cacheExtent,
1128
    List<Widget> children = const <Widget>[],
1129
  }) : gridDelegate = new SliverGridDelegateWithFixedCrossAxisCount(
1130 1131 1132 1133 1134
         crossAxisCount: crossAxisCount,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),
1135 1136
       childrenDelegate = new SliverChildListDelegate(
         children,
1137
         addAutomaticKeepAlives: addAutomaticKeepAlives,
1138 1139
         addRepaintBoundaries: addRepaintBoundaries,
       ), super(
1140 1141 1142
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
1143
    controller: controller,
1144
    primary: primary,
1145 1146 1147
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
1148
    cacheExtent: cacheExtent,
1149
  );
1150

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

1202 1203 1204 1205 1206
  /// 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.
1207 1208
  final SliverGridDelegate gridDelegate;

1209 1210 1211 1212 1213
  /// 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.
1214
  final SliverChildDelegate childrenDelegate;
1215

1216
  @override
1217 1218 1219
  Widget buildChildLayout(BuildContext context) {
    return new SliverGrid(
      delegate: childrenDelegate,
1220 1221
      gridDelegate: gridDelegate,
    );
Adam Barth's avatar
Adam Barth committed
1222 1223
  }
}