| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:math' as math; |
| |
| import 'package:flutter/foundation.dart'; |
| |
| import 'box.dart'; |
| import 'object.dart'; |
| import 'sliver.dart'; |
| import 'sliver_fixed_extent_list.dart'; |
| import 'sliver_multi_box_adaptor.dart'; |
| |
| /// A sliver that contains multiple box children that each fill the viewport. |
| /// |
| /// [RenderSliverFillViewport] places its children in a linear array along the |
| /// main axis. Each child is sized to fill the viewport, both in the main and |
| /// cross axis. A [viewportFraction] factor can be provided to size the children |
| /// to a multiple of the viewport's main axis dimension (typically a fraction |
| /// less than 1.0). |
| /// |
| /// See also: |
| /// |
| /// * [RenderSliverFillRemaining], which sizes the children based on the |
| /// remaining space rather than the viewport itself. |
| /// * [RenderSliverFixedExtentList], which has a configurable [itemExtent]. |
| /// * [RenderSliverList], which does not require its children to have the same |
| /// extent in the main axis. |
| class RenderSliverFillViewport extends RenderSliverFixedExtentBoxAdaptor { |
| /// Creates a sliver that contains multiple box children that each fill the |
| /// viewport. |
| /// |
| /// The [childManager] argument must not be null. |
| RenderSliverFillViewport({ |
| @required RenderSliverBoxChildManager childManager, |
| double viewportFraction = 1.0, |
| }) : assert(viewportFraction != null), |
| assert(viewportFraction > 0.0), |
| _viewportFraction = viewportFraction, |
| super(childManager: childManager); |
| |
| @override |
| double get itemExtent => constraints.viewportMainAxisExtent * viewportFraction; |
| |
| /// The fraction of the viewport that each child should fill in the main axis. |
| /// |
| /// If this fraction is less than 1.0, more than one child will be visible at |
| /// once. If this fraction is greater than 1.0, each child will be larger than |
| /// the viewport in the main axis. |
| double get viewportFraction => _viewportFraction; |
| double _viewportFraction; |
| set viewportFraction(double value) { |
| assert(value != null); |
| if (_viewportFraction == value) |
| return; |
| _viewportFraction = value; |
| markNeedsLayout(); |
| } |
| } |
| |
| /// A sliver that contains a single box child that contains a scrollable and |
| /// fills the viewport. |
| /// |
| /// [RenderSliverFillRemainingWithScrollable] sizes its child to fill the |
| /// viewport in the cross axis and to fill the remaining space in the viewport |
| /// in the main axis. |
| /// |
| /// Typically this will be the last sliver in a viewport, since (by definition) |
| /// there is never any room for anything beyond this sliver. |
| /// |
| /// See also: |
| /// |
| /// * [NestedScrollView], which uses this sliver for the inner scrollable. |
| /// * [RenderSliverFillRemaining], which lays out its |
| /// non-scrollable child slightly different than this widget. |
| /// * [RenderSliverFillRemainingAndOverscroll], which incorporates the |
| /// overscroll into the remaining space to fill. |
| /// * [RenderSliverFillViewport], which sizes its children based on the |
| /// size of the viewport, regardless of what else is in the scroll view. |
| /// * [RenderSliverList], which shows a list of variable-sized children in a |
| /// viewport. |
| class RenderSliverFillRemainingWithScrollable extends RenderSliverSingleBoxAdapter { |
| /// Creates a [RenderSliver] that wraps a scrollable [RenderBox] which is |
| /// sized to fit the remaining space in the viewport. |
| RenderSliverFillRemainingWithScrollable({ RenderBox child }) : super(child: child); |
| |
| @override |
| void performLayout() { |
| final SliverConstraints constraints = this.constraints; |
| // TODO(Piinks): This may fill too much space for NestedScrollView, https://github.com/flutter/flutter/issues/46028 |
| final double extent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0); |
| |
| if (child != null) |
| child.layout(constraints.asBoxConstraints( |
| minExtent: extent, |
| maxExtent: extent, |
| )); |
| |
| final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |
| assert(paintedChildSize.isFinite); |
| assert(paintedChildSize >= 0.0); |
| geometry = SliverGeometry( |
| scrollExtent: constraints.viewportMainAxisExtent, |
| paintExtent: paintedChildSize, |
| maxPaintExtent: paintedChildSize, |
| hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |
| ); |
| if (child != null) |
| setChildParentData(child, constraints, geometry); |
| } |
| } |
| |
| /// A sliver that contains a single box child that is non-scrollable and fills |
| /// the remaining space in the viewport. |
| /// |
| /// [RenderSliverFillRemaining] sizes its child to fill the |
| /// viewport in the cross axis and to fill the remaining space in the viewport |
| /// in the main axis. |
| /// |
| /// Typically this will be the last sliver in a viewport, since (by definition) |
| /// there is never any room for anything beyond this sliver. |
| /// |
| /// See also: |
| /// |
| /// * [RenderSliverFillRemainingWithScrollable], which lays out its scrollable |
| /// child slightly different than this widget. |
| /// * [RenderSliverFillRemainingAndOverscroll], which incorporates the |
| /// overscroll into the remaining space to fill. |
| /// * [RenderSliverFillViewport], which sizes its children based on the |
| /// size of the viewport, regardless of what else is in the scroll view. |
| /// * [RenderSliverList], which shows a list of variable-sized children in a |
| /// viewport. |
| class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter { |
| /// Creates a [RenderSliver] that wraps a non-scrollable [RenderBox] which is |
| /// sized to fit the remaining space in the viewport. |
| RenderSliverFillRemaining({ RenderBox child }) : super(child: child); |
| |
| @override |
| void performLayout() { |
| final SliverConstraints constraints = this.constraints; |
| // The remaining space in the viewportMainAxisExtent. Can be <= 0 if we have |
| // scrolled beyond the extent of the screen. |
| double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent; |
| |
| if (child != null) { |
| double childExtent; |
| switch (constraints.axis) { |
| case Axis.horizontal: |
| childExtent = child.getMaxIntrinsicWidth(constraints.crossAxisExtent); |
| break; |
| case Axis.vertical: |
| childExtent = child.getMaxIntrinsicHeight(constraints.crossAxisExtent); |
| break; |
| } |
| |
| // If the childExtent is greater than the computed extent, we want to use |
| // that instead of potentially cutting off the child. This allows us to |
| // safely specify a maxExtent. |
| extent = math.max(extent, childExtent); |
| child.layout(constraints.asBoxConstraints( |
| minExtent: extent, |
| maxExtent: extent, |
| )); |
| } |
| |
| assert(extent.isFinite, |
| 'The calculated extent for the child of SliverFillRemaining is not finite. ' |
| 'This can happen if the child is a scrollable, in which case, the ' |
| 'hasScrollBody property of SliverFillRemaining should not be set to ' |
| 'false.', |
| ); |
| final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |
| assert(paintedChildSize.isFinite); |
| assert(paintedChildSize >= 0.0); |
| geometry = SliverGeometry( |
| scrollExtent: extent, |
| paintExtent: paintedChildSize, |
| maxPaintExtent: paintedChildSize, |
| hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |
| ); |
| if (child != null) |
| setChildParentData(child, constraints, geometry); |
| } |
| } |
| |
| /// A sliver that contains a single box child that is non-scrollable and fills |
| /// the remaining space in the viewport including any overscrolled area. |
| /// |
| /// [RenderSliverFillRemainingAndOverscroll] sizes its child to fill the |
| /// viewport in the cross axis and to fill the remaining space in the viewport |
| /// in the main axis with the overscroll area included. |
| /// |
| /// Typically this will be the last sliver in a viewport, since (by definition) |
| /// there is never any room for anything beyond this sliver. |
| /// |
| /// See also: |
| /// |
| /// * [RenderSliverFillRemainingWithScrollable], which lays out its scrollable |
| /// child without overscroll. |
| /// * [RenderSliverFillRemaining], which lays out its |
| /// non-scrollable child without overscroll. |
| /// * [RenderSliverFillViewport], which sizes its children based on the |
| /// size of the viewport, regardless of what else is in the scroll view. |
| /// * [RenderSliverList], which shows a list of variable-sized children in a |
| /// viewport. |
| class RenderSliverFillRemainingAndOverscroll extends RenderSliverSingleBoxAdapter { |
| /// Creates a [RenderSliver] that wraps a non-scrollable [RenderBox] which is |
| /// sized to fit the remaining space plus any overscroll in the viewport. |
| RenderSliverFillRemainingAndOverscroll({ RenderBox child }) : super(child: child); |
| |
| @override |
| void performLayout() { |
| final SliverConstraints constraints = this.constraints; |
| // The remaining space in the viewportMainAxisExtent. Can be <= 0 if we have |
| // scrolled beyond the extent of the screen. |
| double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent; |
| // The maxExtent includes any overscrolled area. Can be < 0 if we have |
| // overscroll in the opposite direction, away from the end of the list. |
| double maxExtent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0); |
| |
| if (child != null) { |
| double childExtent; |
| switch (constraints.axis) { |
| case Axis.horizontal: |
| childExtent = child.getMaxIntrinsicWidth(constraints.crossAxisExtent); |
| break; |
| case Axis.vertical: |
| childExtent = child.getMaxIntrinsicHeight(constraints.crossAxisExtent); |
| break; |
| } |
| |
| // If the childExtent is greater than the computed extent, we want to use |
| // that instead of potentially cutting off the child. This allows us to |
| // safely specify a maxExtent. |
| extent = math.max(extent, childExtent); |
| // The extent could be larger than the maxExtent due to a larger child |
| // size or overscrolling at the top of the scrollable (rather than at the |
| // end where this sliver is). |
| maxExtent = math.max(extent, maxExtent); |
| child.layout(constraints.asBoxConstraints(minExtent: extent, maxExtent: maxExtent)); |
| } |
| |
| assert(extent.isFinite, |
| 'The calculated extent for the child of SliverFillRemaining is not finite. ' |
| 'This can happen if the child is a scrollable, in which case, the ' |
| 'hasScrollBody property of SliverFillRemaining should not be set to ' |
| 'false.', |
| ); |
| final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |
| assert(paintedChildSize.isFinite); |
| assert(paintedChildSize >= 0.0); |
| geometry = SliverGeometry( |
| scrollExtent: extent, |
| paintExtent: math.min(maxExtent, constraints.remainingPaintExtent), |
| maxPaintExtent: maxExtent, |
| hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |
| ); |
| if (child != null) |
| setChildParentData(child, constraints, geometry); |
| } |
| } |