mirror of
https://github.com/flutter/flutter
synced 2024-08-24 18:36:03 +00:00
RenderViewport max layout cycles should depend on number of slivers (#144104)
Fixes https://github.com/flutter/flutter/issues/144102 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [Data Driven Fixes]: https://github.com/flutter/flutter/wiki/Data-driven-Fixes
This commit is contained in:
parent
fd73a9a0c6
commit
19087442ce
|
@ -1385,7 +1385,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
|
|||
return constraints.biggest;
|
||||
}
|
||||
|
||||
static const int _maxLayoutCycles = 10;
|
||||
static const int _maxLayoutCyclesPerChild = 10;
|
||||
|
||||
// Out-of-band data computed during layout.
|
||||
late double _minScrollExtent;
|
||||
|
@ -1419,6 +1419,7 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
|
|||
};
|
||||
|
||||
final double centerOffsetAdjustment = center!.centerOffsetAdjustment;
|
||||
final int maxLayoutCycles = _maxLayoutCyclesPerChild * childCount;
|
||||
|
||||
double correction;
|
||||
int count = 0;
|
||||
|
@ -1435,9 +1436,9 @@ class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentDat
|
|||
}
|
||||
}
|
||||
count += 1;
|
||||
} while (count < _maxLayoutCycles);
|
||||
} while (count < maxLayoutCycles);
|
||||
assert(() {
|
||||
if (count >= _maxLayoutCycles) {
|
||||
if (count >= maxLayoutCycles) {
|
||||
assert(count != 1);
|
||||
throw FlutterError(
|
||||
'A RenderViewport exceeded its maximum number of layout cycles.\n'
|
||||
|
|
|
@ -2370,4 +2370,85 @@ void main() {
|
|||
);
|
||||
errors.clear();
|
||||
});
|
||||
|
||||
testWidgets('RenderViewport maxLayoutCycles depends on the number of children',
|
||||
(WidgetTester tester) async {
|
||||
Future<void> expectFlutterError({
|
||||
required Widget widget,
|
||||
required WidgetTester tester,
|
||||
}) async {
|
||||
final List<FlutterErrorDetails> errors = <FlutterErrorDetails>[];
|
||||
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
||||
FlutterError.onError = (FlutterErrorDetails error) => errors.add(error);
|
||||
try {
|
||||
await tester.pumpWidget(widget);
|
||||
} finally {
|
||||
FlutterError.onError = oldHandler;
|
||||
}
|
||||
expect(errors, isNotEmpty);
|
||||
expect(errors.first.exception, isFlutterError);
|
||||
}
|
||||
|
||||
Widget buildWidget({required int sliverCount, required int correctionsCount}) {
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: CustomScrollView(
|
||||
slivers: List<Widget>.generate(
|
||||
sliverCount,
|
||||
(_) => _ScrollOffsetCorrectionSliver(correctionsCount: correctionsCount)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 5 correction per child will pass.
|
||||
await tester.pumpWidget(buildWidget(sliverCount: 30, correctionsCount: 5));
|
||||
|
||||
// 15 correction per child will throw exception.
|
||||
await expectFlutterError(
|
||||
widget: buildWidget(sliverCount: 1, correctionsCount: 15),
|
||||
tester: tester,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Simple sliver that applies N scroll offset corrections.
|
||||
class _RenderScrollOffsetCorrectionSliver extends RenderSliver {
|
||||
int _correctionCount = 0;
|
||||
@override
|
||||
void performLayout() {
|
||||
if (_correctionCount > 0) {
|
||||
--_correctionCount;
|
||||
geometry = const SliverGeometry(scrollOffsetCorrection: 1.0);
|
||||
return;
|
||||
}
|
||||
const double extent = 5;
|
||||
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent);
|
||||
final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: extent);
|
||||
|
||||
geometry = SliverGeometry(
|
||||
scrollExtent: extent,
|
||||
paintExtent: paintedChildSize,
|
||||
maxPaintExtent: extent,
|
||||
cacheExtent: cacheExtent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ScrollOffsetCorrectionSliver extends SingleChildRenderObjectWidget {
|
||||
const _ScrollOffsetCorrectionSliver({required this.correctionsCount});
|
||||
final int correctionsCount;
|
||||
|
||||
@override
|
||||
_RenderScrollOffsetCorrectionSliver createRenderObject(BuildContext context) {
|
||||
final _RenderScrollOffsetCorrectionSliver sliver = _RenderScrollOffsetCorrectionSliver();
|
||||
sliver._correctionCount = correctionsCount;
|
||||
return sliver;
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(BuildContext context, covariant _RenderScrollOffsetCorrectionSliver renderObject) {
|
||||
super.updateRenderObject(context, renderObject);
|
||||
renderObject.markNeedsLayout();
|
||||
renderObject._correctionCount = correctionsCount;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue