Unverified Commit fbeb5e0c authored by chunhtai's avatar chunhtai Committed by GitHub

Fix issue 23527: Exception: RenderViewport exceeded its maximum number of layout cycles (#30809)

parent 8bea3fb2
......@@ -686,7 +686,8 @@ class SliverGeometry extends Diagnosticable {
/// * [RenderViewport.cacheExtent] for a description of a viewport's cache area.
final double cacheExtent;
static const double _epsilon = 1e-10;
/// The epsilon of tolerable double precision error.
static const double precisionErrorTolerance = 1e-10;
/// Asserts that this geometry is internally consistent.
///
......@@ -719,8 +720,8 @@ class SliverGeometry extends Diagnosticable {
}
verify(maxPaintExtent != null, 'The "maxPaintExtent" is null.');
// If the paintExtent is slightly more than the maxPaintExtent, but the difference is still less
// than epsilon, we will not throw the assert below.
if (paintExtent - maxPaintExtent > _epsilon) {
// than precisionErrorTolerance, we will not throw the assert below.
if (paintExtent - maxPaintExtent > precisionErrorTolerance) {
verify(false,
'The "maxPaintExtent" is less than the "paintExtent".\n' +
_debugCompareFloats('maxPaintExtent', maxPaintExtent, 'paintExtent', paintExtent) +
......
......@@ -119,7 +119,8 @@ class RenderSliverList extends RenderSliverMultiBoxAdaptor {
}
final double firstChildScrollOffset = earliestScrollOffset - paintExtentOf(firstChild);
if (firstChildScrollOffset < 0.0) {
// firstChildScrollOffset may contain double precision error
if (firstChildScrollOffset < -SliverGeometry.precisionErrorTolerance) {
// The first child doesn't fit within the viewport (underflow) and
// there may be additional children above it. Find the real first child
// and then correct the scroll position so that there's room for all and
......
......@@ -3,7 +3,9 @@
// found in the LICENSE file.
import 'package:flutter/rendering.dart';
import 'package:flutter/animation.dart';
import 'package:meta/meta.dart';
import '../flutter_test_alternative.dart';
import 'rendering_tester.dart';
......@@ -67,6 +69,49 @@ class TestRenderSliverBoxChildManager extends RenderSliverBoxChildManager {
void setDidUnderflow(bool value) { }
}
class ViewportOffsetSpy extends ViewportOffset {
ViewportOffsetSpy(this._pixels);
double _pixels;
@override
double get pixels => _pixels;
bool corrected = false;
@override
bool applyViewportDimension(double viewportDimension) => true;
@override
bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) => true;
@override
void correctBy(double correction) {
_pixels += correction;
corrected = true;
}
@override
void jumpTo(double pixels) {
// Do nothing, not required in test.
}
@override
Future<void> animateTo(
double to, {
@required Duration duration,
@required Curve curve,
}) async {
// Do nothing, not required in test.
}
@override
ScrollDirection get userScrollDirection => ScrollDirection.idle;
@override
bool get allowImplicitScrolling => false;
}
void main() {
test('RenderSliverList basic test - down', () {
RenderObject inner;
......@@ -254,6 +299,43 @@ void main() {
expect(inner.geometry.scrollOffsetCorrection, isNull);
});
test('SliverList - no correction when tiny double precision error', () {
RenderSliverList inner;
RenderBox a;
final TestRenderSliverBoxChildManager childManager = TestRenderSliverBoxChildManager(
children: <RenderBox>[
a = RenderSizedBox(const Size(100.0, 400.0)),
RenderSizedBox(const Size(100.0, 400.0)),
RenderSizedBox(const Size(100.0, 400.0)),
RenderSizedBox(const Size(100.0, 400.0)),
RenderSizedBox(const Size(100.0, 400.0)),
],
);
inner = childManager.createRenderObject();
final RenderViewport root = RenderViewport(
axisDirection: AxisDirection.down,
crossAxisDirection: AxisDirection.right,
offset: ViewportOffset.zero(),
children: <RenderSliver>[
inner,
],
);
layout(root);
final SliverMultiBoxAdaptorParentData parentData = a.parentData;
// Simulate double precision error.
parentData.layoutOffset = -0.0000000000001;
root.offset = ViewportOffset.fixed(900.0);
pumpFrame();
final ViewportOffsetSpy spy = ViewportOffsetSpy(0.0);
root.offset = spy;
pumpFrame();
expect(spy.corrected, false);
});
test('SliverMultiBoxAdaptorParentData.toString', () {
final SliverMultiBoxAdaptorParentData candidate = SliverMultiBoxAdaptorParentData();
expect(candidate.keepAlive, isFalse);
......
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