Unverified Commit 38e41f5a authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

SliverFillRemaining accounts for child size when hasScrollBody is false (#35810)

Fixes the hasScrollBody flag not accounting for child size. Adds the ability to specify over-scroll behavior.
parent 252491f8
...@@ -7,6 +7,7 @@ import 'dart:math' as math; ...@@ -7,6 +7,7 @@ import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'box.dart'; import 'box.dart';
import 'object.dart';
import 'sliver.dart'; import 'sliver.dart';
import 'sliver_fixed_extent_list.dart'; import 'sliver_fixed_extent_list.dart';
import 'sliver_multi_box_adaptor.dart'; import 'sliver_multi_box_adaptor.dart';
...@@ -114,34 +115,84 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter { ...@@ -114,34 +115,84 @@ class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter {
RenderSliverFillRemaining({ RenderSliverFillRemaining({
RenderBox child, RenderBox child,
this.hasScrollBody = true, this.hasScrollBody = true,
this.fillOverscroll = false,
}) : assert(hasScrollBody != null), }) : assert(hasScrollBody != null),
super(child: child); super(child: child);
/// Whether the child has a scrollable body, this value cannot be null. /// 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 /// Defaults to true such that the child will extend beyond the viewport and
/// scroll, as seen in [NestedScrollView]. /// scroll, as seen in [NestedScrollView].
/// ///
/// Setting this value to false will allow the child to fill the remainder of /// Setting this value to false will allow the child to fill the remainder of
/// the viewport and not extend further. /// 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; 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;
@override @override
void performLayout() { void performLayout() {
final double extent = constraints.remainingPaintExtent double childExtent;
- math.min(constraints.overlap, 0.0) double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent;
// Adding the offset for when this SliverFillRemaining is not scrollable, double maxExtent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);
// so it will stretch to fill on overscroll.
+ (hasScrollBody ? 0.0 : constraints.scrollOffset); if (hasScrollBody) {
extent = maxExtent;
if (child != null) if (child != null)
child.layout(constraints.asBoxConstraints(minExtent: extent, maxExtent: extent), parentUsesSize: true); child.layout(
constraints.asBoxConstraints(
minExtent: extent,
maxExtent: extent,
),
parentUsesSize: true,
);
} else if (child != null) {
child.layout(constraints.asBoxConstraints(), parentUsesSize: true);
switch (constraints.axis) {
case Axis.horizontal:
childExtent = child.size.width;
break;
case Axis.vertical:
childExtent = child.size.height;
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,
);
}
}
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); final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
assert(paintedChildSize.isFinite); assert(paintedChildSize.isFinite);
assert(paintedChildSize >= 0.0); assert(paintedChildSize >= 0.0);
geometry = SliverGeometry( geometry = SliverGeometry(
// 0.0 can be applied here for cases when there is not scroll body since scrollExtent: hasScrollBody ? constraints.viewportMainAxisExtent : extent,
// SliverFillRemaining will not have any slivers following it.
scrollExtent: hasScrollBody ? constraints.viewportMainAxisExtent : 0.0,
paintExtent: paintedChildSize, paintExtent: paintedChildSize,
maxPaintExtent: paintedChildSize, maxPaintExtent: paintedChildSize,
hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,
......
...@@ -1369,23 +1369,44 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget { ...@@ -1369,23 +1369,44 @@ class SliverFillRemaining extends SingleChildRenderObjectWidget {
Key key, Key key,
Widget child, Widget child,
this.hasScrollBody = true, this.hasScrollBody = true,
this.fillOverscroll = false,
}) : assert(hasScrollBody != null), }) : assert(hasScrollBody != null),
super(key: key, child: child); super(key: key, child: child);
/// Whether the child has a scrollable body, this value cannot be null. /// 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 /// Defaults to true such that the child will extend beyond the viewport and
/// scroll, as seen in [NestedScrollView]. /// scroll, as seen in [NestedScrollView].
/// ///
/// Setting this value to false will allow the child to fill the remainder of /// Setting this value to false will allow the child to fill the remainder of
/// the viewport and not extend further. /// 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.
final bool hasScrollBody; 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 @override
RenderSliverFillRemaining createRenderObject(BuildContext context) => RenderSliverFillRemaining(hasScrollBody: hasScrollBody); RenderSliverFillRemaining createRenderObject(BuildContext context) {
return RenderSliverFillRemaining(
hasScrollBody: hasScrollBody,
fillOverscroll: fillOverscroll,
);
}
@override @override
void updateRenderObject(BuildContext context, RenderSliverFillRemaining renderObject) => renderObject.hasScrollBody = hasScrollBody; void updateRenderObject(BuildContext context, RenderSliverFillRemaining renderObject) {
renderObject.hasScrollBody = hasScrollBody;
renderObject.fillOverscroll = fillOverscroll;
}
} }
/// Mark a child as needing to stay alive even when it's in a lazy list that /// Mark a child as needing to stay alive even when it's in a lazy list that
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment