blob: fc51eb6a8baa91f60c7b7e9a702fc6967cc4c907 [file] [log] [blame]
// 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);
}
}