scroll_view.dart 22.2 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.

xster's avatar
xster committed
5
import 'package:flutter/foundation.dart';
Adam Barth's avatar
Adam Barth committed
6 7 8
import 'package:flutter/rendering.dart';

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

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
/// A widget that scrolls.
///
/// Scrollable widgets consist of three pieces:
///
///  1. A [Scrollable] widget, which listens for various user gestures and
///     implements the interaction design for scrolling.
///  2. A viewport widget, such as [Viewport] or [ShrinkWrappingViewport], which
///     implements the visual design for scrolling by displaying only a portion
///     of the widgets inside the scroll view.
///  3. One or more slivers, which are widgets that can be composed to created
///     various scrolling effects, such as lists, grids, and expanding headers.
///
/// [ScrollView] helps orchestrate these pieces by creating the [Scrollable] and
/// the viewport and defering to its subclass to create the slivers.
///
/// See also:
///
///  * [ListView], which is a commonly used [ScrollView] that displays a
///    scrolling, linear list of child widgets.
///  * [PageView], which is a scrolling list of child widgets that are each the
///    size of the viewport.
///  * [GridView], which is a [ScrollView] that displays a scrolling, 2D array
///    of child widgets.
///  * [CustomScrollView], which is a [ScrollView] that creates custom scroll
///    effects using slivers.
43
abstract class ScrollView extends StatelessWidget {
44 45 46
  /// Creates a widget that scrolls.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
Adam Barth's avatar
Adam Barth committed
47 48 49
  ScrollView({
    Key key,
    this.scrollDirection: Axis.vertical,
50
    this.reverse: false,
51
    this.controller,
52
    bool primary,
Adam Barth's avatar
Adam Barth committed
53
    this.physics,
54
    this.shrinkWrap: false,
55 56
  }) : primary = primary ?? controller == null && scrollDirection == Axis.vertical,
       super(key: key) {
57 58
    assert(reverse != null);
    assert(shrinkWrap != null);
59
    assert(this.primary != null);
60
    assert(controller == null || !this.primary,
61
           'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
62 63
           'You cannot both set primary to true and pass an explicit controller.'
    );
64
  }
Adam Barth's avatar
Adam Barth committed
65

66 67 68
  /// The axis along which the scroll view scrolls.
  ///
  /// Defaults to [Axis.vertical].
Adam Barth's avatar
Adam Barth committed
69 70
  final Axis scrollDirection;

71
  /// Whether the scroll view scrolls in the reading direction.
72 73 74 75 76 77
  ///
  /// 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
78
  /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view
79 80 81 82
  /// scrolls from top to bottom when [reverse] is false and from bottom to top
  /// when [reverse] is true.
  ///
  /// Defaults to false.
83 84
  final bool reverse;

85 86 87 88
  /// An object that can be used to control the position to which this scroll
  /// view is scrolled.
  ///
  /// Must be null if [primary] is true.
89 90
  final ScrollController controller;

91 92 93 94 95 96
  /// Whether this is the primary scroll view associated with the parent
  /// [PrimaryScrollController].
  ///
  /// On iOS, this identifies the scroll view that will scroll to top in
  /// response to a tap in the status bar.
  ///
97 98
  /// Defaults to true when [scrollDirection] is [Axis.vertical] and
  /// [controller] is null.
99 100
  final bool primary;

101 102 103 104 105 106
  /// How the scroll view should respond to user input.
  ///
  /// For example, determines how the scroll view continues to animate after the
  /// user stops dragging the scroll view.
  ///
  /// Defaults to matching platform conventions.
Adam Barth's avatar
Adam Barth committed
107 108
  final ScrollPhysics physics;

109 110 111 112 113 114 115 116 117 118 119 120 121 122
  /// 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.
123 124
  final bool shrinkWrap;

125 126 127 128 129 130
  /// Returns the [AxisDirection] in which the scroll view scrolls.
  ///
  /// Combines the [scrollDirection] with the [reverse] boolean to obtain the
  /// concrete [AxisDirection].
  ///
  /// In the future, this function will also consider the reading direction.
131 132 133 134 135 136 137 138 139 140 141 142
  @protected
  AxisDirection getDirection(BuildContext context) {
    // TODO(abarth): Consider reading direction.
    switch (scrollDirection) {
      case Axis.horizontal:
        return reverse ? AxisDirection.left : AxisDirection.right;
      case Axis.vertical:
        return reverse ? AxisDirection.up : AxisDirection.down;
    }
    return null;
  }

143 144
  /// Subclasses should override this method to build the slivers for the inside
  /// of the viewport.
145
  @protected
146
  List<Widget> buildSlivers(BuildContext context);
Adam Barth's avatar
Adam Barth committed
147

148 149
  @override
  Widget build(BuildContext context) {
150 151
    final List<Widget> slivers = buildSlivers(context);
    final AxisDirection axisDirection = getDirection(context);
152

153
    final ScrollController scrollController = primary
154 155
        ? PrimaryScrollController.of(context)
        : controller;
156
    final Scrollable scrollable = new Scrollable(
157
      axisDirection: axisDirection,
158
      controller: scrollController,
Adam Barth's avatar
Adam Barth committed
159
      physics: physics,
160 161 162 163 164
      viewportBuilder: (BuildContext context, ViewportOffset offset) {
        if (shrinkWrap) {
          return new ShrinkWrappingViewport(
            axisDirection: axisDirection,
            offset: offset,
165
            slivers: slivers,
166 167
          );
        } else {
Adam Barth's avatar
Adam Barth committed
168
          return new Viewport(
169 170
            axisDirection: axisDirection,
            offset: offset,
171
            slivers: slivers,
172 173 174
          );
        }
      }
175
    );
176 177 178
    return primary && scrollController != null
      ? new PrimaryScrollController.none(child: scrollable)
      : scrollable;
179
  }
180 181 182 183 184

  @override
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('$scrollDirection');
185 186 187 188 189 190 191 192
    if (reverse)
      description.add('reversed');
    if (controller != null)
      description.add('$controller');
    if (primary)
      description.add('using primary controller');
    if (physics != null)
      description.add('$physics');
193 194 195 196 197
    if (shrinkWrap)
      description.add('shrink-wrapping');
  }
}

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
/// A [ScrollView] that creates custom scroll effects using slivers.
///
/// A [CustomScrollView] lets you supply [slivers] directly to create various
/// scrolling effects, such as lists, grids, and expanding headers. For example,
/// to create a scroll view that contains an expanding app bar followed by a
/// list and a grid, use a list of three slivers: [SliverAppBar], [SliverList],
/// and [SliverGrid].
///
/// See also:
///
///  * [SliverList], which is a sliver that displays linear list of children.
///  * [SliverFixedExtentList], which is a more efficient sliver that displays
///    linear list of children that have the same extent along the scroll axis.
///  * [SliverGrid], which is a sliver that displays a 2D array of children.
///  * [SliverPadding], which is a sliver that adds blank space around another
///    sliver.
///  * [SliverAppBar], which is a sliver that displays a header that can expand
///    and float as the scroll view scrolls.
Adam Barth's avatar
Adam Barth committed
216
class CustomScrollView extends ScrollView {
217 218 219
  /// 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
220 221 222 223
  CustomScrollView({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
224
    ScrollController controller,
225
    bool primary,
Adam Barth's avatar
Adam Barth committed
226 227 228 229 230 231 232
    ScrollPhysics physics,
    bool shrinkWrap: false,
    this.slivers: const <Widget>[],
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
233
    controller: controller,
234
    primary: primary,
Adam Barth's avatar
Adam Barth committed
235 236 237 238
    physics: physics,
    shrinkWrap: shrinkWrap,
  );

239
  /// The slivers to place inside the viewport.
Adam Barth's avatar
Adam Barth committed
240 241 242 243 244 245
  final List<Widget> slivers;

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

246 247 248 249 250 251 252 253
/// A [ScrollView] uses a single child layout model.
///
/// See also:
///
///  * [ListView], which is a [BoxScrollView] that uses a linear layout model.
///  * [GridView], which is a [BoxScrollView] that uses a 2D layout model.
///  * [CustomScrollView], which can combine multiple child layout models into a
///    single scroll view.
254
abstract class BoxScrollView extends ScrollView {
255 256 257
  /// Creates a [ScrollView] uses a single child layout model.
  ///
  /// If the [primary] argument is true, the [controller] must be null.
258 259 260 261
  BoxScrollView({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
262
    ScrollController controller,
263
    bool primary,
264 265 266 267 268 269 270
    ScrollPhysics physics,
    bool shrinkWrap: false,
    this.padding,
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
271
    controller: controller,
272
    primary: primary,
273 274 275 276
    physics: physics,
    shrinkWrap: shrinkWrap,
  );

277
  /// The amount of space by which to inset the children.
278 279 280 281 282 283
  final EdgeInsets padding;

  @override
  List<Widget> buildSlivers(BuildContext context) {
    Widget sliver = buildChildLayout(context);
    if (padding != null)
284
      sliver = new SliverPadding(padding: padding, sliver: sliver);
285 286 287
    return <Widget>[ sliver ];
  }

288
  /// Subclasses should override this method to build the layout model.
289 290 291 292 293 294
  @protected
  Widget buildChildLayout(BuildContext context);

  @override
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
295 296
    if (padding != null)
      description.add('padding: $padding');
297 298 299
  }
}

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
/// A scrollable, linear list of widgets.
///
/// [ListView] is the most commonly used scrolling widget. It displays its
/// children one after another in the scroll direction. In the cross axis, the
/// children are required to fill the [ListView].
///
/// If non-null, the [itemExtent] forces the children to have the given extent
/// in the scroll direction. Specifying an [itemExtent] is more efficient than
/// letting the children determine their own extent because the scrolling
/// machinery can make use of the foreknowledge of the children's extent to save
/// work, for example when the scroll position changes drastically.
///
/// There are three options for constructing a [ListView]:
///
///  1. The default constuctor takes an explict [List<Widget>] of children. This
///     constructor is appropriate for list views with a small number of
///     children because constructing the [List] requires doing work for every
///     child that could possibly be displayed in the list view instead of just
///     those children that are actually visible.
///
///  2. The [ListView.builder] takes an [IndexedWidgetBuilder], which builds the
///     children on demand. This constructor is appropriate for list views with
///     a large (or infinite) number of children because the builder is called
///     only for those children that are actually visible.
///
///  3. The [ListView.custom] takes a [SliverChildDelegate], which provides the
///     ability to customize additional aspects of the child model. For example,
///     a [SliverChildDelegate] can control the algorithm used to estimate the
///     size of children that are not actually visible.
329 330 331
///
/// See also:
///
332 333 334 335 336 337 338
///  * [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.
339
class ListView extends BoxScrollView {
340 341 342 343 344 345
  /// 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.
346 347 348 349
  ListView({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
350
    ScrollController controller,
351
    bool primary,
352 353 354 355 356 357 358 359 360
    ScrollPhysics physics,
    bool shrinkWrap: false,
    EdgeInsets padding,
    this.itemExtent,
    List<Widget> children: const <Widget>[],
  }) : childrenDelegate = new SliverChildListDelegate(children), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
361
    controller: controller,
362
    primary: primary,
363 364 365 366 367
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  );

368 369 370 371 372 373 374 375 376 377 378
  /// Creates a scrollable, linear array of widgets that are created on demand.
  ///
  /// This constructor is appropriate for list views with a large (or infinite)
  /// number of children because the builder is called only for those children
  /// that are actually visible.
  ///
  /// Providing a non-null [itemCount] improves the ability of the [ListView] to
  /// estimate the maximum scroll extent.
  ///
  /// [itemBuilder] will be called only with indices greater than or equal to
  /// zero and less than [itemCount].
379 380 381 382
  ListView.builder({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
383
    ScrollController controller,
384
    bool primary,
385 386 387 388 389 390 391 392 393 394
    ScrollPhysics physics,
    bool shrinkWrap: false,
    EdgeInsets padding,
    this.itemExtent,
    IndexedWidgetBuilder itemBuilder,
    int itemCount,
  }) : childrenDelegate = new SliverChildBuilderDelegate(itemBuilder, childCount: itemCount), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
395
    controller: controller,
396
    primary: primary,
397 398 399 400 401
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  );

402 403 404 405
  /// 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.
406 407 408 409
  ListView.custom({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
410
    ScrollController controller,
411
    bool primary,
412 413 414 415 416 417 418 419 420
    ScrollPhysics physics,
    bool shrinkWrap: false,
    EdgeInsets padding,
    this.itemExtent,
    @required this.childrenDelegate,
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
421
    controller: controller,
422
    primary: primary,
423 424 425 426 427 428 429
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  ) {
    assert(childrenDelegate != null);
  }

430 431 432 433 434 435 436
  /// 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.
437 438
  final double itemExtent;

439 440 441 442 443 444
  /// 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.
445
  final SliverChildDelegate childrenDelegate;
446 447 448 449

  @override
  Widget buildChildLayout(BuildContext context) {
    if (itemExtent != null) {
450
      return new SliverFixedExtentList(
451 452 453 454
        delegate: childrenDelegate,
        itemExtent: itemExtent,
      );
    }
455
    return new SliverList(delegate: childrenDelegate);
456 457 458 459 460
  }

  @override
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
461 462 463
    if (itemExtent != null)
      description.add('itemExtent: $itemExtent');
  }
464 465
}

466 467 468 469 470 471 472 473 474 475 476 477 478 479
/// A scrollable, 2D array of widgets.
///
/// The most commonly used grid layouts are [GridView.count], which creates a
/// layout with a fixed number of tiles in the cross axis, and
/// [GridView.extent], which creates a layout with tiles that have a maximum
/// cross-axis extent. A custom [SliverGridDelegate] can produce an aribtrary 2D
/// arrangement of children, including arrangements that are unaligned or
/// overlapping.
///
/// To create a grid with a large (or infinite) number of children, use the
/// [GridView.custom] constructor with either a [SliverChildBuilderDelegate] or
/// a custom [SliverChildDelegate].
///
/// To create a linear array of children, use a [ListView].
480 481 482
///
/// See also:
///
483 484 485 486 487 488 489 490 491 492 493
///  * [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.
494
class GridView extends BoxScrollView {
495 496 497 498
  /// Creates a scrollable, 2D array of widgets with a custom
  /// [SliverGridDelegate].
  ///
  /// The [gridDelegate] argument must not be null.
499
  GridView({
500
    Key key,
501
    Axis scrollDirection: Axis.vertical,
502
    bool reverse: false,
503
    ScrollController controller,
504
    bool primary,
505
    ScrollPhysics physics,
506
    bool shrinkWrap: false,
507 508
    EdgeInsets padding,
    @required this.gridDelegate,
509
    List<Widget> children: const <Widget>[],
510 511 512 513
  }) : childrenDelegate = new SliverChildListDelegate(children), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
514
    controller: controller,
515
    primary: primary,
516 517 518 519 520 521
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  ) {
    assert(gridDelegate != null);
  }
522

523 524 525 526 527 528 529
  /// Creates a scrollable, 2D array of widgets with both a custom
  /// [SliverGridDelegate] and a custom [SliverChildDelegate].
  ///
  /// To use an [IndexedWidgetBuilder] callback to build children, use the
  /// [SliverChildBuilderDelegate].
  ///
  /// The [gridDelegate] and [childrenDelegate] arguments must not be null.
530
  GridView.custom({
531
    Key key,
532
    Axis scrollDirection: Axis.vertical,
533
    bool reverse: false,
534
    ScrollController controller,
535
    bool primary,
536 537
    ScrollPhysics physics,
    bool shrinkWrap: false,
538
    EdgeInsets padding,
539 540 541 542 543 544
    @required this.gridDelegate,
    @required this.childrenDelegate,
  }) : super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
545
    controller: controller,
546
    primary: primary,
547 548 549 550 551 552 553 554
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  ) {
    assert(gridDelegate != null);
    assert(childrenDelegate != null);
  }

555 556 557 558
  /// Creates a scrollable, 2D array of widgets with a fixed number of tiles in
  /// the cross axis.
  ///
  /// Uses a [SliverGridDelegateWithFixedCrossAxisCount] as the [gridDelegate].
559 560 561 562
  GridView.count({
    Key key,
    Axis scrollDirection: Axis.vertical,
    bool reverse: false,
563
    ScrollController controller,
564
    bool primary,
565
    ScrollPhysics physics,
566
    bool shrinkWrap: false,
567
    EdgeInsets padding,
568 569 570 571
    @required int crossAxisCount,
    double mainAxisSpacing: 0.0,
    double crossAxisSpacing: 0.0,
    double childAspectRatio: 1.0,
572
    List<Widget> children: const <Widget>[],
573
  }) : gridDelegate = new SliverGridDelegateWithFixedCrossAxisCount(
574 575 576 577 578 579 580 581 582
         crossAxisCount: crossAxisCount,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),
       childrenDelegate = new SliverChildListDelegate(children), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
583
    controller: controller,
584
    primary: primary,
585 586 587 588
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  );
589

590 591 592 593
  /// Creates a scrollable, 2D array of widgets with tiles that have a maximum
  /// cross-axis extent.
  ///
  /// Uses a [SliverGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
594
  GridView.extent({
595
    Key key,
596
    Axis scrollDirection: Axis.vertical,
597
    bool reverse: false,
598
    ScrollController controller,
599
    bool primary,
600
    ScrollPhysics physics,
601
    bool shrinkWrap: false,
602
    EdgeInsets padding,
603 604 605 606
    @required double maxCrossAxisExtent,
    double mainAxisSpacing: 0.0,
    double crossAxisSpacing: 0.0,
    double childAspectRatio: 1.0,
607
    List<Widget> children: const <Widget>[],
608
  }) : gridDelegate = new SliverGridDelegateWithMaxCrossAxisExtent(
609 610 611 612 613 614 615 616 617
         maxCrossAxisExtent: maxCrossAxisExtent,
         mainAxisSpacing: mainAxisSpacing,
         crossAxisSpacing: crossAxisSpacing,
         childAspectRatio: childAspectRatio,
       ),
       childrenDelegate = new SliverChildListDelegate(children), super(
    key: key,
    scrollDirection: scrollDirection,
    reverse: reverse,
618
    controller: controller,
619
    primary: primary,
620 621 622 623
    physics: physics,
    shrinkWrap: shrinkWrap,
    padding: padding,
  );
624

625 626 627 628 629
  /// 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.
630 631
  final SliverGridDelegate gridDelegate;

632 633 634 635 636
  /// 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.
637
  final SliverChildDelegate childrenDelegate;
638

639
  @override
640 641 642
  Widget buildChildLayout(BuildContext context) {
    return new SliverGrid(
      delegate: childrenDelegate,
643 644
      gridDelegate: gridDelegate,
    );
Adam Barth's avatar
Adam Barth committed
645 646
  }
}