From f2ea7304c8ca0a3d6efa1da4ea487eca6746d827 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 3 Feb 2017 16:43:04 -0800 Subject: [PATCH] PageView shouldn't squish when overscrolled (#7870) This patch passes down the viewportMainAxisExtent so that RenderSliverPage can pick it up and size its children appropriately. --- .../flutter/lib/src/rendering/sliver.dart | 29 ++++++++++++--- .../lib/src/rendering/sliver_list.dart | 2 +- .../test/rendering/slivers_helpers_test.dart | 3 ++ .../flutter/test/widgets/page_view_test.dart | 35 +++++++++++++++++++ 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/packages/flutter/lib/src/rendering/sliver.dart b/packages/flutter/lib/src/rendering/sliver.dart index 4d810542a5c..ff3095cb3fd 100644 --- a/packages/flutter/lib/src/rendering/sliver.dart +++ b/packages/flutter/lib/src/rendering/sliver.dart @@ -139,6 +139,7 @@ class SliverConstraints extends Constraints { @required this.overlap, @required this.remainingPaintExtent, @required this.crossAxisExtent, + @required this.viewportMainAxisExtent, }); SliverConstraints copyWith({ @@ -149,6 +150,7 @@ class SliverConstraints extends Constraints { double overlap, double remainingPaintExtent, double crossAxisExtent, + double viewportMainAxisExtent, }) { return new SliverConstraints( axisDirection: axisDirection ?? this.axisDirection, @@ -158,6 +160,7 @@ class SliverConstraints extends Constraints { overlap: overlap ?? this.overlap, remainingPaintExtent: remainingPaintExtent ?? this.remainingPaintExtent, crossAxisExtent: crossAxisExtent ?? this.crossAxisExtent, + viewportMainAxisExtent: viewportMainAxisExtent ?? this.viewportMainAxisExtent, ); } @@ -239,10 +242,16 @@ class SliverConstraints extends Constraints { /// bottom of a downwards vertical viewport. final double remainingPaintExtent; - /// The number of pixels in the cross-axis. For a vertical list, this is the - /// width of the viewport. + /// The number of pixels in the cross-axis. + /// + /// For a vertical list, this is the width of the sliver.. final double crossAxisExtent; + /// The number of pixels the viewport can display in the main axis. + /// + /// For a vertical list, this is the height of the viewport. + final double viewportMainAxisExtent; + Axis get axis => axisDirectionToAxis(axisDirection); /// Return what the [growthDirection] would be if the [axisDirection] was @@ -280,6 +289,7 @@ class SliverConstraints extends Constraints { bool get isNormalized { return scrollOffset >= 0.0 && crossAxisExtent >= 0.0 + && viewportMainAxisExtent >= 0.0 && remainingPaintExtent >= 0.0; } @@ -320,8 +330,10 @@ class SliverConstraints extends Constraints { assert(overlap != null); assert(remainingPaintExtent != null); assert(crossAxisExtent != null); + assert(viewportMainAxisExtent != null); assert(scrollOffset >= 0.0); assert(crossAxisExtent >= 0.0); + assert(viewportMainAxisExtent >= 0.0); assert(remainingPaintExtent >= 0.0); assert(isNormalized); // should be redundant with earlier checks return true; @@ -340,12 +352,13 @@ class SliverConstraints extends Constraints { scrollOffset == typedOther.scrollOffset && overlap == typedOther.overlap && remainingPaintExtent == typedOther.remainingPaintExtent && - crossAxisExtent == typedOther.crossAxisExtent; + crossAxisExtent == typedOther.crossAxisExtent && + viewportMainAxisExtent == typedOther.viewportMainAxisExtent; } @override int get hashCode { - return hashValues(axisDirection, growthDirection, scrollOffset, overlap, remainingPaintExtent, crossAxisExtent); + return hashValues(axisDirection, growthDirection, scrollOffset, overlap, remainingPaintExtent, crossAxisExtent, viewportMainAxisExtent); } @override @@ -357,7 +370,8 @@ class SliverConstraints extends Constraints { 'scrollOffset: ${scrollOffset.toStringAsFixed(1)}, ' 'remainingPaintExtent: ${remainingPaintExtent.toStringAsFixed(1)}, ' + (overlap != 0.0 ? 'overlap: ${overlap.toStringAsFixed(1)}, ' : '') + - 'crossAxisExtent: ${crossAxisExtent.toStringAsFixed(1)}' + 'crossAxisExtent: ${crossAxisExtent.toStringAsFixed(1)}' + + 'viewportMainAxisExtent: ${viewportMainAxisExtent.toStringAsFixed(1)}' + ')'; } } @@ -1178,6 +1192,7 @@ abstract class RenderViewportBase2 constraints.remainingPaintExtent; + double get itemExtent => constraints.viewportMainAxisExtent; } diff --git a/packages/flutter/test/rendering/slivers_helpers_test.dart b/packages/flutter/test/rendering/slivers_helpers_test.dart index 92c91dce941..196e12758d0 100644 --- a/packages/flutter/test/rendering/slivers_helpers_test.dart +++ b/packages/flutter/test/rendering/slivers_helpers_test.dart @@ -25,6 +25,7 @@ void main() { overlap: 0.0, remainingPaintExtent: 0.0, crossAxisExtent: 0.0, + viewportMainAxisExtent: 0.0, ); SliverConstraints b = a.copyWith(); expect(a, equals(b)); @@ -41,6 +42,7 @@ void main() { overlap: 20.0, remainingPaintExtent: 30.0, crossAxisExtent: 40.0, + viewportMainAxisExtent: 30.0, ); SliverConstraints d = new SliverConstraints( axisDirection: AxisDirection.up, @@ -50,6 +52,7 @@ void main() { overlap: 20.0, remainingPaintExtent: 30.0, crossAxisExtent: 40.0, + viewportMainAxisExtent: 30.0, ); expect(c, equals(d)); expect(c.normalizedGrowthDirection, equals(GrowthDirection.forward)); diff --git a/packages/flutter/test/widgets/page_view_test.dart b/packages/flutter/test/widgets/page_view_test.dart index 412d0c13a67..ddff7b946f7 100644 --- a/packages/flutter/test/widgets/page_view_test.dart +++ b/packages/flutter/test/widgets/page_view_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'states.dart'; @@ -73,4 +74,38 @@ void main() { expect(find.text('Alaska'), findsOneWidget); expect(find.text('Arizona'), findsNothing); }); + + testWidgets('PageView does not squish when overscrolled', (WidgetTester tester) async { + await tester.pumpWidget(new MaterialApp( + theme: new ThemeData(platform: TargetPlatform.iOS), + home: new PageView( + children: new List.generate(10, (int i) { + return new Container( + key: new ValueKey(i), + decoration: const BoxDecoration( + backgroundColor: const Color(0xFF0000FF), + ), + ); + }), + ), + )); + + Size sizeOf(int i) => tester.getSize(find.byKey(new ValueKey(i))); + double leftOf(int i) => tester.getTopLeft(find.byKey(new ValueKey(i))).x; + + expect(leftOf(0), equals(0.0)); + expect(sizeOf(0), equals(const Size(800.0, 600.0))); + + await tester.scroll(find.byType(PageView), const Offset(100.0, 0.0)); + await tester.pump(); + + expect(leftOf(0), equals(100.0)); + expect(sizeOf(0), equals(const Size(800.0, 600.0))); + + await tester.scroll(find.byType(PageView), const Offset(-200.0, 0.0)); + await tester.pump(); + + expect(leftOf(0), equals(-100.0)); + expect(sizeOf(0), equals(const Size(800.0, 600.0))); + }); }