Reland "Fix sliver geometry assert (#47027)"

This commit is contained in:
LongCatIsLooong 2019-12-19 14:27:35 -08:00 committed by GitHub
parent 648a5d8a1e
commit e3c8ecaf7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 6 deletions

View File

@ -1211,17 +1211,17 @@ abstract class RenderSliver extends RenderObject {
} }
)); ));
assert(() { assert(() {
if (geometry.paintExtent > constraints.remainingPaintExtent) { if (geometry.paintOrigin + geometry.paintExtent > constraints.remainingPaintExtent) {
throw FlutterError.fromParts(<DiagnosticsNode>[ throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.'), ErrorSummary('SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.'),
describeForError('The render object whose geometry violates the constraints is the following'), describeForError('The render object whose geometry violates the constraints is the following'),
..._debugCompareFloats( ..._debugCompareFloats(
'remainingPaintExtent', constraints.remainingPaintExtent, 'remainingPaintExtent', constraints.remainingPaintExtent,
'paintExtent', geometry.paintExtent, 'paintOrigin + paintExtent', geometry.paintOrigin + geometry.paintExtent,
), ),
ErrorDescription( ErrorDescription(
'The paintExtent must cause the child sliver to paint within the viewport, and so ' 'The paintOrigin and paintExtent must cause the child sliver to paint '
'cannot exceed the remainingPaintExtent.', 'within the viewport, and so cannot exceed the remainingPaintExtent.',
), ),
]); ]);
} }

View File

@ -373,14 +373,15 @@ abstract class RenderSliverPinnedPersistentHeader extends RenderSliverPersistent
final bool overlapsContent = constraints.overlap > 0.0; final bool overlapsContent = constraints.overlap > 0.0;
excludeFromSemanticsScrolling = overlapsContent || (constraints.scrollOffset > maxExtent - minExtent); excludeFromSemanticsScrolling = overlapsContent || (constraints.scrollOffset > maxExtent - minExtent);
layoutChild(constraints.scrollOffset, maxExtent, overlapsContent: overlapsContent); layoutChild(constraints.scrollOffset, maxExtent, overlapsContent: overlapsContent);
final double layoutExtent = (maxExtent - constraints.scrollOffset).clamp(0.0, constraints.remainingPaintExtent) as double; final double effectiveRemainingPaintExtent = math.max(0, constraints.remainingPaintExtent - constraints.overlap);
final double layoutExtent = (maxExtent - constraints.scrollOffset).clamp(0.0, effectiveRemainingPaintExtent) as double;
final double stretchOffset = stretchConfiguration != null ? final double stretchOffset = stretchConfiguration != null ?
constraints.overlap.abs() : constraints.overlap.abs() :
0.0; 0.0;
geometry = SliverGeometry( geometry = SliverGeometry(
scrollExtent: maxExtent, scrollExtent: maxExtent,
paintOrigin: constraints.overlap, paintOrigin: constraints.overlap,
paintExtent: math.min(childExtent, constraints.remainingPaintExtent), paintExtent: math.min(childExtent, effectiveRemainingPaintExtent),
layoutExtent: layoutExtent, layoutExtent: layoutExtent,
maxPaintExtent: maxExtent + stretchOffset, maxPaintExtent: maxExtent + stretchOffset,
maxScrollObstructionExtent: minExtent, maxScrollObstructionExtent: minExtent,

View File

@ -1028,6 +1028,30 @@ void main() {
debugDefaultTargetPlatformOverride = null; debugDefaultTargetPlatformOverride = null;
}, },
); );
testWidgets('Should not crash when dragged', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
CupertinoSliverRefreshControl(
onRefresh: () async => Future<void>.delayed(const Duration(days: 2000)),
),
],
),
),
);
await tester.dragFrom(const Offset(100, 10), const Offset(0.0, 50.0), touchSlopY: 0);
await tester.pump();
await tester.dragFrom(const Offset(100, 10), const Offset(0, 500), touchSlopY: 0);
await tester.pump();
expect(tester.takeException(), isNull);
});
}; };
final VoidCallback stateMachineTestGroup = () { final VoidCallback stateMachineTestGroup = () {
@ -1465,6 +1489,36 @@ void main() {
// Test the internal state machine directly to make sure the UI aren't just // Test the internal state machine directly to make sure the UI aren't just
// correct by coincidence. // correct by coincidence.
group('state machine test short list', stateMachineTestGroup); group('state machine test short list', stateMachineTestGroup);
testWidgets(
'Does not crash when paintExtent > remainingPaintExtent',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/46871.
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
const CupertinoSliverRefreshControl(),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => const SizedBox(height: 100),
childCount: 20,
),
),
],
),
),
);
// Drag the content down far enough so that
// geometry.paintExent > constraints.maxPaintExtent
await tester.dragFrom(const Offset(10, 10), const Offset(0, 500));
await tester.pump();
expect(tester.takeException(), isNull);
});
} }
class MockHelper extends Mock { class MockHelper extends Mock {