Refactor SliverFillRemaining (#47379)
This commit is contained in:
committed by
Flutter GitHub Bot
parent
5005824763
commit
76b21d2867
@@ -59,69 +59,90 @@ class RenderSliverFillViewport extends RenderSliverFixedExtentBoxAdaptor {
|
||||
}
|
||||
}
|
||||
|
||||
/// A sliver that contains a single box child that fills the remaining space in
|
||||
/// the viewport.
|
||||
/// A sliver that contains a single box child that contains a scrollable and
|
||||
/// fills 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.
|
||||
/// [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() {
|
||||
// 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 [RenderBox] which is sized to fit
|
||||
/// the remaining space in the viewport.
|
||||
RenderSliverFillRemaining({
|
||||
RenderBox child,
|
||||
this.hasScrollBody = true,
|
||||
this.fillOverscroll = false,
|
||||
}) : assert(hasScrollBody != null),
|
||||
super(child: child);
|
||||
|
||||
/// Indicates whether the child has a scrollable body, this value cannot be
|
||||
/// null.
|
||||
///
|
||||
/// Defaults to true such that the child will extend beyond the viewport and
|
||||
/// scroll, as seen in [NestedScrollView].
|
||||
///
|
||||
/// Setting this value to false will allow the child to fill the remainder of
|
||||
/// the viewport and not extend further. However, if the
|
||||
/// [precedingScrollExtent] exceeds the size of the viewport, the sliver will
|
||||
/// defer to the child's size rather than overriding it.
|
||||
bool hasScrollBody;
|
||||
|
||||
/// Indicates whether the child should stretch to fill the overscroll area
|
||||
/// created by certain scroll physics, such as iOS' default scroll physics.
|
||||
/// This value cannot be null. This flag is only relevant when the
|
||||
/// [hasScrollBody] value is false.
|
||||
///
|
||||
/// Defaults to false, meaning the default behavior is for the child to
|
||||
/// maintain its size and not extend into the overscroll area.
|
||||
bool fillOverscroll;
|
||||
/// 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() {
|
||||
double childExtent;
|
||||
// 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;
|
||||
double maxExtent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);
|
||||
|
||||
if (hasScrollBody) {
|
||||
extent = maxExtent;
|
||||
if (child != null)
|
||||
child.layout(
|
||||
constraints.asBoxConstraints(
|
||||
minExtent: extent,
|
||||
maxExtent: extent,
|
||||
),
|
||||
parentUsesSize: true,
|
||||
);
|
||||
} else if (child != null) {
|
||||
if (child != null) {
|
||||
double childExtent;
|
||||
switch (constraints.axis) {
|
||||
case Axis.horizontal:
|
||||
childExtent = child.getMaxIntrinsicWidth(constraints.crossAxisExtent);
|
||||
@@ -131,34 +152,27 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter {
|
||||
break;
|
||||
}
|
||||
|
||||
if (constraints.precedingScrollExtent > constraints.viewportMainAxisExtent || childExtent > extent)
|
||||
extent = childExtent;
|
||||
if (maxExtent < extent)
|
||||
maxExtent = extent;
|
||||
if ((fillOverscroll ? maxExtent : extent) > childExtent) {
|
||||
child.layout(
|
||||
constraints.asBoxConstraints(
|
||||
minExtent: extent,
|
||||
maxExtent: fillOverscroll ? maxExtent : extent,
|
||||
),
|
||||
parentUsesSize: true,
|
||||
);
|
||||
} else {
|
||||
child.layout(constraints.asBoxConstraints(), parentUsesSize: true);
|
||||
}
|
||||
// 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.',
|
||||
'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: hasScrollBody ? constraints.viewportMainAxisExtent : extent,
|
||||
scrollExtent: extent,
|
||||
paintExtent: paintedChildSize,
|
||||
maxPaintExtent: paintedChildSize,
|
||||
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
|
||||
@@ -167,3 +181,79 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter {
|
||||
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() {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import 'scroll_metrics.dart';
|
||||
import 'scroll_physics.dart';
|
||||
import 'scroll_position.dart';
|
||||
import 'scroll_view.dart';
|
||||
import 'sliver.dart';
|
||||
import 'sliver_fill.dart';
|
||||
import 'ticker_provider.dart';
|
||||
import 'viewport.dart';
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import 'scroll_position_with_single_context.dart';
|
||||
import 'scroll_view.dart';
|
||||
import 'scrollable.dart';
|
||||
import 'sliver.dart';
|
||||
import 'sliver_fill.dart';
|
||||
import 'viewport.dart';
|
||||
|
||||
/// A controller for [PageView].
|
||||
|
||||
@@ -1025,148 +1025,6 @@ class SliverGrid extends SliverMultiBoxAdaptorWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A sliver that contains multiple box children that each fills the viewport.
|
||||
///
|
||||
/// [SliverFillViewport] 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.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverFixedExtentList], which has a configurable
|
||||
/// [SliverFixedExtentList.itemExtent].
|
||||
/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
|
||||
/// except that it uses a prototype list item instead of a pixel value to define
|
||||
/// the main axis extent of each item.
|
||||
/// * [SliverList], which does not require its children to have the same
|
||||
/// extent in the main axis.
|
||||
class SliverFillViewport extends StatelessWidget {
|
||||
/// Creates a sliver whose box children that each fill the viewport.
|
||||
const SliverFillViewport({
|
||||
Key key,
|
||||
@required this.delegate,
|
||||
this.viewportFraction = 1.0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction > 0.0),
|
||||
super(key: key);
|
||||
|
||||
/// 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.
|
||||
final double viewportFraction;
|
||||
|
||||
/// {@macro flutter.widgets.sliverMultiBoxAdaptor.delegate}
|
||||
final SliverChildDelegate delegate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _SliverFractionalPadding(
|
||||
viewportFraction: (1 - viewportFraction).clamp(0, 1) / 2,
|
||||
sliver: _SliverFillViewportRenderObjectWidget(
|
||||
viewportFraction: viewportFraction,
|
||||
delegate: delegate,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverFillViewportRenderObjectWidget extends SliverMultiBoxAdaptorWidget {
|
||||
const _SliverFillViewportRenderObjectWidget({
|
||||
Key key,
|
||||
@required SliverChildDelegate delegate,
|
||||
this.viewportFraction = 1.0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction > 0.0),
|
||||
super(key: key, delegate: delegate);
|
||||
|
||||
final double viewportFraction;
|
||||
|
||||
@override
|
||||
RenderSliverFillViewport createRenderObject(BuildContext context) {
|
||||
final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
|
||||
return RenderSliverFillViewport(childManager: element, viewportFraction: viewportFraction);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderSliverFillViewport renderObject) {
|
||||
renderObject.viewportFraction = viewportFraction;
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverFractionalPadding extends SingleChildRenderObjectWidget {
|
||||
const _SliverFractionalPadding({
|
||||
Key key,
|
||||
this.viewportFraction = 0,
|
||||
Widget sliver,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction >= 0),
|
||||
assert(viewportFraction <= 0.5),
|
||||
super(key: key, child: sliver);
|
||||
|
||||
final double viewportFraction;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) => _RenderSliverFractionalPadding(viewportFraction: viewportFraction);
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, _RenderSliverFractionalPadding renderObject) {
|
||||
renderObject.viewportFraction = viewportFraction;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderSliverFractionalPadding extends RenderSliverEdgeInsetsPadding {
|
||||
_RenderSliverFractionalPadding({
|
||||
double viewportFraction = 0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction <= 0.5),
|
||||
assert(viewportFraction >= 0),
|
||||
_viewportFraction = viewportFraction;
|
||||
|
||||
double get viewportFraction => _viewportFraction;
|
||||
double _viewportFraction;
|
||||
set viewportFraction(double newValue) {
|
||||
assert(newValue != null);
|
||||
if (_viewportFraction == newValue)
|
||||
return;
|
||||
_viewportFraction = newValue;
|
||||
_markNeedsResolution();
|
||||
}
|
||||
|
||||
@override
|
||||
EdgeInsets get resolvedPadding => _resolvedPadding;
|
||||
EdgeInsets _resolvedPadding;
|
||||
|
||||
void _markNeedsResolution() {
|
||||
_resolvedPadding = null;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
void _resolve() {
|
||||
if (_resolvedPadding != null)
|
||||
return;
|
||||
assert(constraints.axis != null);
|
||||
final double paddingValue = constraints.viewportMainAxisExtent * viewportFraction;
|
||||
switch (constraints.axis) {
|
||||
case Axis.horizontal:
|
||||
_resolvedPadding = EdgeInsets.symmetric(horizontal: paddingValue);
|
||||
break;
|
||||
case Axis.vertical:
|
||||
_resolvedPadding = EdgeInsets.symmetric(vertical: paddingValue);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
_resolve();
|
||||
super.performLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// An element that lazily builds children for a [SliverMultiBoxAdaptorWidget].
|
||||
///
|
||||
/// Implements [RenderSliverBoxChildManager], which lets this element manage
|
||||
@@ -1456,275 +1314,6 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
|
||||
}
|
||||
}
|
||||
|
||||
/// A sliver that contains a single box child that fills the remaining space in
|
||||
/// the viewport.
|
||||
///
|
||||
/// [SliverFillRemaining] will size its [child] to fill the viewport in the
|
||||
/// cross axis. The extent of the sliver and its child's size in the main axis
|
||||
/// is computed conditionally, described in further detail below.
|
||||
///
|
||||
/// Typically this will be the last sliver in a viewport, since (by definition)
|
||||
/// there is never any room for anything beyond this sliver.
|
||||
///
|
||||
/// ## Main Axis Extent
|
||||
///
|
||||
/// ### When [SliverFillRemaining] has a scrollable child
|
||||
///
|
||||
/// The [hasScrollBody] flag indicates whether the sliver's child has a
|
||||
/// scrollable body. This value is never null, and defaults to true. A common
|
||||
/// example of this use is a [NestedScrollView]. In this case, the sliver will
|
||||
/// size its child to fill the maximum available extent.
|
||||
///
|
||||
/// ### When [SliverFillRemaining] does not have a scrollable child
|
||||
///
|
||||
/// When [hasScrollBody] is set to false, the child's size is taken into account
|
||||
/// when considering the extent to which it should fill the space. The
|
||||
/// [precedingScrollExtent] of the [SliverConstraints] is also taken into
|
||||
/// account in deciding how to layout the sliver.
|
||||
///
|
||||
/// * [SliverFillRemaining] will size its [child] to fill the viewport in the
|
||||
/// main axis if that space is larger than the child's extent, and the
|
||||
/// [precedingScrollExtent] has not exceeded the main axis extent of the
|
||||
/// viewport.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_sizes_child.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] sizes its [child] to fill the
|
||||
/// remaining extent of the viewport in both axes. The icon is centered in the
|
||||
/// sliver, and would be in any computed extent for the sliver.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverToBoxAdapter(
|
||||
/// child: Container(
|
||||
/// color: Colors.amber[300],
|
||||
/// height: 150.0,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// color: Colors.blue[100],
|
||||
/// child: Icon(
|
||||
/// Icons.sentiment_very_satisfied,
|
||||
/// size: 75,
|
||||
/// color: Colors.blue[900],
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// * [SliverFillRemaining] will defer to the size of its [child] if the
|
||||
/// child's size exceeds the remaining space in the viewport.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_defers_to_child.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] defers to the size of its [child]
|
||||
/// because the child's extent exceeds that of the remaining extent of the
|
||||
/// viewport's main axis.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverFixedExtentList(
|
||||
/// itemExtent: 100.0,
|
||||
/// delegate: SliverChildBuilderDelegate(
|
||||
/// (BuildContext context, int index) {
|
||||
/// return Container(
|
||||
/// color: index % 2 == 0
|
||||
/// ? Colors.amber[200]
|
||||
/// : Colors.blue[200],
|
||||
/// );
|
||||
/// },
|
||||
/// childCount: 3,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// color: Colors.orange[300],
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(50.0),
|
||||
/// child: FlutterLogo(size: 100),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// * [SliverFillRemaining] will defer to the size of its [child] if the
|
||||
/// [precedingScrollExtent] exceeded the length of the viewport's main axis.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_scrolled_beyond.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] defers to the size of its [child]
|
||||
/// because the [precedingScrollExtent] of the [SliverConstraints] has gone
|
||||
/// beyond that of the viewport's main axis.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverFixedExtentList(
|
||||
/// itemExtent: 130.0,
|
||||
/// delegate: SliverChildBuilderDelegate(
|
||||
/// (BuildContext context, int index) {
|
||||
/// return Container(
|
||||
/// color: index % 2 == 0
|
||||
/// ? Colors.indigo[200]
|
||||
/// : Colors.orange[200],
|
||||
/// );
|
||||
/// },
|
||||
/// childCount: 5,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(50.0),
|
||||
/// child: Icon(
|
||||
/// Icons.pan_tool,
|
||||
/// size: 60,
|
||||
/// color: Colors.blueGrey,
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// * For [ScrollPhysics] that allow overscroll, such as
|
||||
/// [BouncingScrollPhysics], setting the [fillOverscroll] flag to true allows
|
||||
/// the size of the [child] to _stretch_, filling the overscroll area. It does
|
||||
/// this regardless of the path chosen to provide the child's size.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_fill_overscroll.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining]'s child stretches to fill the
|
||||
/// overscroll area when [fillOverscroll] is true. This sample also features a
|
||||
/// button that is pinned to the bottom of the sliver, regardless of size or
|
||||
/// overscroll behavior. Try switching [fillOverscroll] to see the difference.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// // The ScrollPhysics are overridden here to illustrate the functionality
|
||||
/// // of fillOverscroll on all devices this sample may be run on.
|
||||
/// // fillOverscroll only changes the behavior of your layout when applied
|
||||
/// // to Scrollables that allow for overscroll. BouncingScrollPhysics are
|
||||
/// // one example, which are provided by default on the iOS platform.
|
||||
/// physics: BouncingScrollPhysics(),
|
||||
/// slivers: <Widget>[
|
||||
/// SliverToBoxAdapter(
|
||||
/// child: Container(
|
||||
/// color: Colors.tealAccent[700],
|
||||
/// height: 150.0,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// // Switch for different overscroll behavior in your layout.
|
||||
/// // If your ScrollPhysics do not allow for overscroll, setting
|
||||
/// // fillOverscroll to true will have no effect.
|
||||
/// fillOverscroll: true,
|
||||
/// child: Container(
|
||||
/// color: Colors.teal[100],
|
||||
/// child: Align(
|
||||
/// alignment: Alignment.bottomCenter,
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(16.0),
|
||||
/// child: RaisedButton(
|
||||
/// onPressed: () {
|
||||
/// /* Place your onPressed code here! */
|
||||
/// },
|
||||
/// child: Text('Bottom Pinned Button!'),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverFillViewport], which sizes its children based on the
|
||||
/// size of the viewport, regardless of what else is in the scroll view.
|
||||
/// * [SliverList], which shows a list of variable-sized children in a
|
||||
/// viewport.
|
||||
class SliverFillRemaining extends SingleChildRenderObjectWidget {
|
||||
/// Creates a sliver that fills the remaining space in the viewport.
|
||||
const SliverFillRemaining({
|
||||
Key key,
|
||||
Widget child,
|
||||
this.hasScrollBody = true,
|
||||
this.fillOverscroll = false,
|
||||
}) : assert(hasScrollBody != null),
|
||||
super(key: key, child: child);
|
||||
|
||||
/// Indicates whether the child has a scrollable body, this value cannot be
|
||||
/// null.
|
||||
///
|
||||
/// Defaults to true such that the child will extend beyond the viewport and
|
||||
/// scroll, as seen in [NestedScrollView].
|
||||
///
|
||||
/// Setting this value to false will allow the child to fill the remainder of
|
||||
/// the viewport and not extend further. However, if the
|
||||
/// [precedingScrollExtent] of the [SliverContraints] and/or the [child]'s
|
||||
/// extent exceeds the size of the viewport, the sliver will defer to the
|
||||
/// child's size rather than overriding it.
|
||||
final bool hasScrollBody;
|
||||
|
||||
/// Indicates whether the child should stretch to fill the overscroll area
|
||||
/// created by certain scroll physics, such as iOS' default scroll physics.
|
||||
/// This value cannot be null. This flag is only relevant when the
|
||||
/// [hasScrollBody] value is false.
|
||||
///
|
||||
/// Defaults to false, meaning the default behavior is for the child to
|
||||
/// maintain its size and not extend into the overscroll area.
|
||||
final bool fillOverscroll;
|
||||
|
||||
@override
|
||||
RenderSliverFillRemaining createRenderObject(BuildContext context) {
|
||||
return RenderSliverFillRemaining(
|
||||
hasScrollBody: hasScrollBody,
|
||||
fillOverscroll: fillOverscroll,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderSliverFillRemaining renderObject) {
|
||||
renderObject.hasScrollBody = hasScrollBody;
|
||||
renderObject.fillOverscroll = fillOverscroll;
|
||||
}
|
||||
}
|
||||
|
||||
/// A sliver widget that makes its sliver child partially transparent.
|
||||
///
|
||||
/// This class paints its sliver child into an intermediate buffer and then
|
||||
|
||||
467
packages/flutter/lib/src/widgets/sliver_fill.dart
Normal file
467
packages/flutter/lib/src/widgets/sliver_fill.dart
Normal file
@@ -0,0 +1,467 @@
|
||||
// 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 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
import 'basic.dart';
|
||||
import 'framework.dart';
|
||||
import 'sliver.dart';
|
||||
|
||||
/// A sliver that contains multiple box children that each fills the viewport.
|
||||
///
|
||||
/// [SliverFillViewport] 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.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverFixedExtentList], which has a configurable
|
||||
/// [SliverFixedExtentList.itemExtent].
|
||||
/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
|
||||
/// except that it uses a prototype list item instead of a pixel value to define
|
||||
/// the main axis extent of each item.
|
||||
/// * [SliverList], which does not require its children to have the same
|
||||
/// extent in the main axis.
|
||||
class SliverFillViewport extends StatelessWidget {
|
||||
/// Creates a sliver whose box children that each fill the viewport.
|
||||
const SliverFillViewport({
|
||||
Key key,
|
||||
@required this.delegate,
|
||||
this.viewportFraction = 1.0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction > 0.0),
|
||||
super(key: key);
|
||||
|
||||
/// 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.
|
||||
final double viewportFraction;
|
||||
|
||||
/// {@macro flutter.widgets.sliverMultiBoxAdaptor.delegate}
|
||||
final SliverChildDelegate delegate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _SliverFractionalPadding(
|
||||
viewportFraction: (1 - viewportFraction).clamp(0, 1) / 2,
|
||||
sliver: _SliverFillViewportRenderObjectWidget(
|
||||
viewportFraction: viewportFraction,
|
||||
delegate: delegate,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverFillViewportRenderObjectWidget extends SliverMultiBoxAdaptorWidget {
|
||||
const _SliverFillViewportRenderObjectWidget({
|
||||
Key key,
|
||||
@required SliverChildDelegate delegate,
|
||||
this.viewportFraction = 1.0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction > 0.0),
|
||||
super(key: key, delegate: delegate);
|
||||
|
||||
final double viewportFraction;
|
||||
|
||||
@override
|
||||
RenderSliverFillViewport createRenderObject(BuildContext context) {
|
||||
final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
|
||||
return RenderSliverFillViewport(childManager: element, viewportFraction: viewportFraction);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, RenderSliverFillViewport renderObject) {
|
||||
renderObject.viewportFraction = viewportFraction;
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverFractionalPadding extends SingleChildRenderObjectWidget {
|
||||
const _SliverFractionalPadding({
|
||||
this.viewportFraction = 0,
|
||||
Widget sliver,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction >= 0),
|
||||
assert(viewportFraction <= 0.5),
|
||||
super(child: sliver);
|
||||
|
||||
final double viewportFraction;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) => _RenderSliverFractionalPadding(viewportFraction: viewportFraction);
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, _RenderSliverFractionalPadding renderObject) {
|
||||
renderObject.viewportFraction = viewportFraction;
|
||||
}
|
||||
}
|
||||
|
||||
class _RenderSliverFractionalPadding extends RenderSliverEdgeInsetsPadding {
|
||||
_RenderSliverFractionalPadding({
|
||||
double viewportFraction = 0,
|
||||
}) : assert(viewportFraction != null),
|
||||
assert(viewportFraction <= 0.5),
|
||||
assert(viewportFraction >= 0),
|
||||
_viewportFraction = viewportFraction;
|
||||
|
||||
double get viewportFraction => _viewportFraction;
|
||||
double _viewportFraction;
|
||||
set viewportFraction(double newValue) {
|
||||
assert(newValue != null);
|
||||
if (_viewportFraction == newValue)
|
||||
return;
|
||||
_viewportFraction = newValue;
|
||||
_markNeedsResolution();
|
||||
}
|
||||
|
||||
@override
|
||||
EdgeInsets get resolvedPadding => _resolvedPadding;
|
||||
EdgeInsets _resolvedPadding;
|
||||
|
||||
void _markNeedsResolution() {
|
||||
_resolvedPadding = null;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
void _resolve() {
|
||||
if (_resolvedPadding != null)
|
||||
return;
|
||||
assert(constraints.axis != null);
|
||||
final double paddingValue = constraints.viewportMainAxisExtent * viewportFraction;
|
||||
switch (constraints.axis) {
|
||||
case Axis.horizontal:
|
||||
_resolvedPadding = EdgeInsets.symmetric(horizontal: paddingValue);
|
||||
break;
|
||||
case Axis.vertical:
|
||||
_resolvedPadding = EdgeInsets.symmetric(vertical: paddingValue);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
_resolve();
|
||||
super.performLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/// A sliver that contains a single box child that fills the remaining space in
|
||||
/// the viewport.
|
||||
///
|
||||
/// [SliverFillRemaining] will size its [child] to fill the viewport in the
|
||||
/// cross axis. The extent of the sliver and its child's size in the main axis
|
||||
/// is computed conditionally, described in further detail below.
|
||||
///
|
||||
/// Typically this will be the last sliver in a viewport, since (by definition)
|
||||
/// there is never any room for anything beyond this sliver.
|
||||
///
|
||||
/// ## Main Axis Extent
|
||||
///
|
||||
/// ### When [SliverFillRemaining] has a scrollable child
|
||||
///
|
||||
/// The [hasScrollBody] flag indicates whether the sliver's child has a
|
||||
/// scrollable body. This value is never null, and defaults to true. A common
|
||||
/// example of this use is a [NestedScrollView]. In this case, the sliver will
|
||||
/// size its child to fill the maximum available extent.
|
||||
///
|
||||
/// ### When [SliverFillRemaining] does not have a scrollable child
|
||||
///
|
||||
/// When [hasScrollBody] is set to false, the child's size is taken into account
|
||||
/// when considering the extent to which it should fill the space. The extent to
|
||||
/// which the preceding slivers have been scrolled is also taken into
|
||||
/// account in deciding how to layout this sliver.
|
||||
///
|
||||
/// [SliverFillRemaining] will size its [child] to fill the viewport in the
|
||||
/// main axis if that space is larger than the child's extent, and the
|
||||
/// the amount of space that has been scrolled beforehand has not exceeded the
|
||||
/// main axis extent of the viewport.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_sizes_child.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] sizes its [child] to fill the
|
||||
/// remaining extent of the viewport in both axes. The icon is centered in the
|
||||
/// sliver, and would be in any computed extent for the sliver.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverToBoxAdapter(
|
||||
/// child: Container(
|
||||
/// color: Colors.amber[300],
|
||||
/// height: 150.0,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// color: Colors.blue[100],
|
||||
/// child: Icon(
|
||||
/// Icons.sentiment_very_satisfied,
|
||||
/// size: 75,
|
||||
/// color: Colors.blue[900],
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// [SliverFillRemaining] will defer to the size of its [child] if the
|
||||
/// child's size exceeds the remaining space in the viewport.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_defers_to_child.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] defers to the size of its [child]
|
||||
/// because the child's extent exceeds that of the remaining extent of the
|
||||
/// viewport's main axis.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverFixedExtentList(
|
||||
/// itemExtent: 100.0,
|
||||
/// delegate: SliverChildBuilderDelegate(
|
||||
/// (BuildContext context, int index) {
|
||||
/// return Container(
|
||||
/// color: index % 2 == 0
|
||||
/// ? Colors.amber[200]
|
||||
/// : Colors.blue[200],
|
||||
/// );
|
||||
/// },
|
||||
/// childCount: 3,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// color: Colors.orange[300],
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(50.0),
|
||||
/// child: FlutterLogo(size: 100),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// [SliverFillRemaining] will defer to the size of its [child] if the
|
||||
/// [precedingScrollExtent] exceeded the length of the viewport's main axis.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_scrolled_beyond.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining] defers to the size of its [child]
|
||||
/// because the [precedingScrollExtent] of the [SliverConstraints] has gone
|
||||
/// beyond that of the viewport's main axis.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// slivers: <Widget>[
|
||||
/// SliverFixedExtentList(
|
||||
/// itemExtent: 130.0,
|
||||
/// delegate: SliverChildBuilderDelegate(
|
||||
/// (BuildContext context, int index) {
|
||||
/// return Container(
|
||||
/// color: index % 2 == 0
|
||||
/// ? Colors.indigo[200]
|
||||
/// : Colors.orange[200],
|
||||
/// );
|
||||
/// },
|
||||
/// childCount: 5,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// child: Container(
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(50.0),
|
||||
/// child: Icon(
|
||||
/// Icons.pan_tool,
|
||||
/// size: 60,
|
||||
/// color: Colors.blueGrey,
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// For [ScrollPhysics] that allow overscroll, such as
|
||||
/// [BouncingScrollPhysics], setting the [fillOverscroll] flag to true allows
|
||||
/// the size of the [child] to _stretch_, filling the overscroll area. It does
|
||||
/// this regardless of the path chosen to provide the child's size.
|
||||
///
|
||||
/// {@animation 250 500 https://flutter.github.io/assets-for-api-docs/assets/widgets/sliver_fill_remaining_fill_overscroll.mp4}
|
||||
///
|
||||
/// {@tool snippet --template=stateless_widget_scaffold}
|
||||
///
|
||||
/// In this sample the [SliverFillRemaining]'s child stretches to fill the
|
||||
/// overscroll area when [fillOverscroll] is true. This sample also features a
|
||||
/// button that is pinned to the bottom of the sliver, regardless of size or
|
||||
/// overscroll behavior. Try switching [fillOverscroll] to see the difference.
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return CustomScrollView(
|
||||
/// // The ScrollPhysics are overridden here to illustrate the functionality
|
||||
/// // of fillOverscroll on all devices this sample may be run on.
|
||||
/// // fillOverscroll only changes the behavior of your layout when applied
|
||||
/// // to Scrollables that allow for overscroll. BouncingScrollPhysics are
|
||||
/// // one example, which are provided by default on the iOS platform.
|
||||
/// physics: BouncingScrollPhysics(),
|
||||
/// slivers: <Widget>[
|
||||
/// SliverToBoxAdapter(
|
||||
/// child: Container(
|
||||
/// color: Colors.tealAccent[700],
|
||||
/// height: 150.0,
|
||||
/// ),
|
||||
/// ),
|
||||
/// SliverFillRemaining(
|
||||
/// hasScrollBody: false,
|
||||
/// // Switch for different overscroll behavior in your layout.
|
||||
/// // If your ScrollPhysics do not allow for overscroll, setting
|
||||
/// // fillOverscroll to true will have no effect.
|
||||
/// fillOverscroll: true,
|
||||
/// child: Container(
|
||||
/// color: Colors.teal[100],
|
||||
/// child: Align(
|
||||
/// alignment: Alignment.bottomCenter,
|
||||
/// child: Padding(
|
||||
/// padding: const EdgeInsets.all(16.0),
|
||||
/// child: RaisedButton(
|
||||
/// onPressed: () {
|
||||
/// /* Place your onPressed code here! */
|
||||
/// },
|
||||
/// child: Text('Bottom Pinned Button!'),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ),
|
||||
/// ],
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverFillViewport], which sizes its children based on the
|
||||
/// size of the viewport, regardless of what else is in the scroll view.
|
||||
/// * [SliverList], which shows a list of variable-sized children in a
|
||||
/// viewport.
|
||||
class SliverFillRemaining extends StatelessWidget {
|
||||
/// Creates a sliver that fills the remaining space in the viewport.
|
||||
const SliverFillRemaining({
|
||||
Key key,
|
||||
this.child,
|
||||
this.hasScrollBody = true,
|
||||
this.fillOverscroll = false,
|
||||
}) : assert(hasScrollBody != null),
|
||||
assert(fillOverscroll != null),
|
||||
super(key: key);
|
||||
|
||||
/// Doc
|
||||
final Widget child;
|
||||
|
||||
/// Indicates whether the child has a scrollable body, this value cannot be
|
||||
/// null.
|
||||
///
|
||||
/// Defaults to true such that the child will extend beyond the viewport and
|
||||
/// scroll, as seen in [NestedScrollView].
|
||||
///
|
||||
/// Setting this value to false will allow the child to fill the remainder of
|
||||
/// the viewport and not extend further. However, if the
|
||||
/// [precedingScrollExtent] of the [SliverConstraints] and/or the [child]'s
|
||||
/// extent exceeds the size of the viewport, the sliver will defer to the
|
||||
/// child's size rather than overriding it.
|
||||
final bool hasScrollBody;
|
||||
|
||||
/// Indicates whether the child should stretch to fill the overscroll area
|
||||
/// created by certain scroll physics, such as iOS' default scroll physics.
|
||||
/// This value cannot be null. This flag is only relevant when the
|
||||
/// [hasScrollBody] value is false.
|
||||
///
|
||||
/// Defaults to false, meaning the default behavior is for the child to
|
||||
/// maintain its size and not extend into the overscroll area.
|
||||
final bool fillOverscroll;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (hasScrollBody)
|
||||
return _SliverFillRemainingWithScrollable(child: child);
|
||||
if (!fillOverscroll)
|
||||
return _SliverFillRemainingWithoutScrollable(child: child);
|
||||
return _SliverFillRemainingAndOverscroll(child: child);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(
|
||||
DiagnosticsProperty<Widget>(
|
||||
'child',
|
||||
child,
|
||||
)
|
||||
);
|
||||
final List<String> flags = <String>[
|
||||
if (hasScrollBody) 'scrollable',
|
||||
if (fillOverscroll) 'fillOverscroll',
|
||||
];
|
||||
if (flags.isEmpty)
|
||||
flags.add('nonscrollable');
|
||||
properties.add(IterableProperty<String>('mode', flags));
|
||||
}
|
||||
}
|
||||
|
||||
class _SliverFillRemainingWithScrollable extends SingleChildRenderObjectWidget {
|
||||
const _SliverFillRemainingWithScrollable({
|
||||
Key key,
|
||||
Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
RenderSliverFillRemainingWithScrollable createRenderObject(BuildContext context) => RenderSliverFillRemainingWithScrollable();
|
||||
}
|
||||
|
||||
class _SliverFillRemainingWithoutScrollable extends SingleChildRenderObjectWidget {
|
||||
const _SliverFillRemainingWithoutScrollable({
|
||||
Key key,
|
||||
Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
RenderSliverFillRemaining createRenderObject(BuildContext context) => RenderSliverFillRemaining();
|
||||
}
|
||||
|
||||
class _SliverFillRemainingAndOverscroll extends SingleChildRenderObjectWidget {
|
||||
const _SliverFillRemainingAndOverscroll({
|
||||
Key key,
|
||||
Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
RenderSliverFillRemainingAndOverscroll createRenderObject(BuildContext context) => RenderSliverFillRemainingAndOverscroll();
|
||||
}
|
||||
@@ -95,6 +95,7 @@ export 'src/widgets/shortcuts.dart';
|
||||
export 'src/widgets/single_child_scroll_view.dart';
|
||||
export 'src/widgets/size_changed_layout_notifier.dart';
|
||||
export 'src/widgets/sliver.dart';
|
||||
export 'src/widgets/sliver_fill.dart';
|
||||
export 'src/widgets/sliver_layout_builder.dart';
|
||||
export 'src/widgets/sliver_persistent_header.dart';
|
||||
export 'src/widgets/sliver_prototype_extent_list.dart';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user