Unverified Commit ce4d635a authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Fix visual overflow when overscrolling RenderShrinkWrappingViewport (#91620)

parent 42eb9032
......@@ -1919,7 +1919,10 @@ class RenderShrinkWrappingViewport extends RenderViewportBase<SliverLogicalConta
assert(correctedOffset.isFinite);
_maxScrollExtent = 0.0;
_shrinkWrapExtent = 0.0;
_hasVisualOverflow = false;
// Since the viewport is shrinkwrapped, we know that any negative overscroll
// into the potentially infinite mainAxisExtent will overflow the end of
// the viewport.
_hasVisualOverflow = correctedOffset < 0.0;
switch (cacheExtentStyle) {
case CacheExtentStyle.pixel:
_calculatedCacheExtent = cacheExtent;
......@@ -1928,6 +1931,7 @@ class RenderShrinkWrappingViewport extends RenderViewportBase<SliverLogicalConta
_calculatedCacheExtent = mainAxisExtent * _cacheExtent;
break;
}
return layoutChildSequence(
child: firstChild,
scrollOffset: math.max(0.0, correctedOffset),
......
......@@ -1846,7 +1846,8 @@ void main() {
});
});
Widget _buildShrinkWrap({
group('Overscrolling RenderShrinkWrappingViewport', () {
Widget _buildSimpleShrinkWrap({
ScrollController? controller,
Axis scrollDirection = Axis.vertical,
ScrollPhysics? physics,
......@@ -1868,19 +1869,27 @@ void main() {
);
}
testWidgets('Constrained Shrinkwrapping viewport will not overflow on overscroll', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/89717
final ScrollController controller = ScrollController();
await tester.pumpWidget(
Directionality(
Widget _buildClippingShrinkWrap(
ScrollController controller, {
bool constrain = false,
}) {
return Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: Container(
color: const Color(0xFF000000),
child: Column(
children: <Widget>[
Container(height: 100, color: const Color(0x00000000)),
// Translucent boxes above and below the shrinkwrapped viewport
// make it easily discernible if the viewport is not being
// clipped properly.
Opacity(
opacity: 0.5,
child: Container(height: 100, color: const Color(0xFF00B0FF)),
),
Container(
height: 150,
height: constrain ? 150 : null,
color: const Color(0xFFF44336),
child: ListView.builder(
controller: controller,
......@@ -1890,37 +1899,77 @@ void main() {
itemCount: 10,
),
),
Container(height: 100, color: const Color(0x00000000)),
Opacity(
opacity: 0.5,
child: Container(height: 100, color: const Color(0xFF00B0FF)),
),
],
),
),
)
),
);
}
testWidgets('constrained viewport correctly clips overflow', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/89717
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildClippingShrinkWrap(controller, constrain: true)
);
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 100.0);
expect(tester.getTopLeft(find.text('Item 9')).dy, 226.0);
// Overscroll
final TestGesture overscrollGesture = await tester.startGesture(tester.getCenter(find.text('Item 0')));
await overscrollGesture.moveBy(const Offset(0, 25));
await overscrollGesture.moveBy(const Offset(0, 100));
await tester.pump();
expect(controller.offset, -25.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 125.0);
expect(controller.offset, -100.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 200.0);
await expectLater(
find.byType(Directionality),
matchesGoldenFile('shrinkwrapped_overscroll.png'),
matchesGoldenFile('shrinkwrap_clipped_constrained_overscroll.png'),
);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 100.0);
expect(tester.getTopLeft(find.text('Item 9')).dy, 226.0);
});
testWidgets('Shrinkwrap allows overscrolling on default platforms - vertical', (WidgetTester tester) async {
testWidgets('correctly clips overflow without constraints', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/89717
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildClippingShrinkWrap(controller)
);
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 100.0);
expect(tester.getTopLeft(find.text('Item 9')).dy, 226.0);
// Overscroll
final TestGesture overscrollGesture = await tester.startGesture(tester.getCenter(find.text('Item 0')));
await overscrollGesture.moveBy(const Offset(0, 100));
await tester.pump();
expect(controller.offset, -100.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 200.0);
await expectLater(
find.byType(Directionality),
matchesGoldenFile('shrinkwrap_clipped_overscroll.png'),
);
await overscrollGesture.up();
await tester.pumpAndSettle();
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 100.0);
expect(tester.getTopLeft(find.text('Item 9')).dy, 226.0);
});
testWidgets('allows overscrolling on default platforms - vertical', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/10949
// Scrollables should overscroll by default on iOS and macOS
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildShrinkWrap(controller: controller),
_buildSimpleShrinkWrap(controller: controller),
);
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 0.0);
......@@ -1954,12 +2003,12 @@ void main() {
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Shrinkwrap allows overscrolling on default platforms - horizontal', (WidgetTester tester) async {
testWidgets('allows overscrolling on default platforms - horizontal', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/10949
// Scrollables should overscroll by default on iOS and macOS
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildShrinkWrap(controller: controller, scrollDirection: Axis.horizontal),
_buildSimpleShrinkWrap(controller: controller, scrollDirection: Axis.horizontal),
);
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dx, 0.0);
......@@ -1993,12 +2042,12 @@ void main() {
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Shrinkwrap allows overscrolling per physics - vertical', (WidgetTester tester) async {
testWidgets('allows overscrolling per physics - vertical', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/10949
// Scrollables should overscroll when the scroll physics allow
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildShrinkWrap(controller: controller, physics: const BouncingScrollPhysics()),
_buildSimpleShrinkWrap(controller: controller, physics: const BouncingScrollPhysics()),
);
expect(controller.offset, 0.0);
expect(tester.getTopLeft(find.text('Item 0')).dy, 0.0);
......@@ -2032,12 +2081,12 @@ void main() {
expect(tester.getBottomLeft(find.text('Item 19')).dy, 600.0);
});
testWidgets('Shrinkwrap allows overscrolling per physics - horizontal', (WidgetTester tester) async {
testWidgets('allows overscrolling per physics - horizontal', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/10949
// Scrollables should overscroll when the scroll physics allow
final ScrollController controller = ScrollController();
await tester.pumpWidget(
_buildShrinkWrap(
_buildSimpleShrinkWrap(
controller: controller,
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
......@@ -2074,6 +2123,7 @@ void main() {
expect(controller.offset, maxExtent);
expect(tester.getTopRight(find.text('Item 19')).dx, 800.0);
});
});
testWidgets('Handles infinite constraints when TargetPlatform is iOS or macOS', (WidgetTester tester) async {
// regression test for https://github.com/flutter/flutter/issues/45866
......
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