Avoid NaN for shrinkwrapping viewports (#46265)
This commit is contained in:
@@ -505,7 +505,16 @@ abstract class RenderViewportBase<ParentDataClass extends ContainerParentDataMix
|
||||
@override
|
||||
Rect describeApproximatePaintClip(RenderSliver child) {
|
||||
final Rect viewportClip = Offset.zero & size;
|
||||
if (child.constraints.overlap == 0) {
|
||||
// The child's viewportMainAxisExtent can be infinite when a
|
||||
// RenderShrinkWrappingViewport is given infinite constraints, such as when
|
||||
// it is the child of a Row or Column (depending on orientation).
|
||||
//
|
||||
// For example, a shrink wrapping render sliver may have infinite
|
||||
// constraints along the viewport's main axis but may also have bouncing
|
||||
// scroll physics, which will allow for some scrolling effect to occur.
|
||||
// We should just use the viewportClip - the start of the overlap is at
|
||||
// double.infinity and so it is effectively meaningless.
|
||||
if (child.constraints.overlap == 0 || !child.constraints.viewportMainAxisExtent.isFinite) {
|
||||
return viewportClip;
|
||||
}
|
||||
|
||||
@@ -1756,6 +1765,11 @@ class RenderShrinkWrappingViewport extends RenderViewportBase<SliverLogicalConta
|
||||
}
|
||||
|
||||
double _attemptLayout(double mainAxisExtent, double crossAxisExtent, double correctedOffset) {
|
||||
// We can't assert mainAxisExtent is finite, because it could be infinite if
|
||||
// it is within a column or row for example. In such a case, there's not
|
||||
// even any scrolling to do, although some scroll physics (i.e.
|
||||
// BouncingScrollPhysics) could still temporarily scroll the content in a
|
||||
// simulation.
|
||||
assert(!mainAxisExtent.isNaN);
|
||||
assert(mainAxisExtent >= 0.0);
|
||||
assert(crossAxisExtent.isFinite);
|
||||
|
||||
@@ -124,4 +124,47 @@ void main() {
|
||||
layout(renderViewport);
|
||||
expect(renderViewport.describeSemanticsClip(null), rectExpandedOnAxis(height * 2.5));
|
||||
});
|
||||
|
||||
test('RenderShrinkWrappingViewport describeApproximatePaintClip with infinite viewportMainAxisExtent returns finite rect', () {
|
||||
final RenderSliver child = CustomConstraintsRenderSliver(const SliverConstraints(
|
||||
axisDirection: AxisDirection.down,
|
||||
cacheOrigin: 0.0,
|
||||
crossAxisDirection: AxisDirection.left,
|
||||
crossAxisExtent: 400.0,
|
||||
growthDirection: GrowthDirection.forward,
|
||||
overlap: 1.0, // must not equal 0 for this test
|
||||
precedingScrollExtent: 0.0,
|
||||
remainingPaintExtent: double.infinity,
|
||||
remainingCacheExtent: 0.0,
|
||||
scrollOffset: 0.0,
|
||||
userScrollDirection: ScrollDirection.idle,
|
||||
viewportMainAxisExtent: double.infinity, // must == infinity
|
||||
));
|
||||
|
||||
final RenderShrinkWrappingViewport viewport = RenderShrinkWrappingViewport(
|
||||
crossAxisDirection: AxisDirection.left,
|
||||
offset: ViewportOffset.zero(),
|
||||
children: <RenderSliver>[ child ],
|
||||
);
|
||||
|
||||
layout(viewport);
|
||||
expect(
|
||||
viewport.describeApproximatePaintClip(child),
|
||||
const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class CustomConstraintsRenderSliver extends RenderSliver {
|
||||
CustomConstraintsRenderSliver(this.constraints);
|
||||
|
||||
@override
|
||||
SliverGeometry get geometry => const SliverGeometry();
|
||||
|
||||
@override
|
||||
final SliverConstraints constraints;
|
||||
|
||||
@override
|
||||
void performLayout() {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1144,4 +1144,39 @@ void main() {
|
||||
' its intrinsic dimensions.\n',
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Handles infinite constraints when TargetPlatform is iOS', (WidgetTester tester) async {
|
||||
// regression test for https://github.com/flutter/flutter/issues/45866
|
||||
final TargetPlatform oldTargetPlatform = debugDefaultTargetPlatformOverride;
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
GridView(
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 3,
|
||||
mainAxisSpacing: 3,
|
||||
crossAxisSpacing: 3),
|
||||
children: const <Widget>[
|
||||
Text('a'),
|
||||
Text('b'),
|
||||
Text('c'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('b'), findsOneWidget);
|
||||
await tester.drag(find.text('b'), const Offset(0, 200));
|
||||
await tester.pumpAndSettle();
|
||||
debugDefaultTargetPlatformOverride = oldTargetPlatform;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user