From bd2d3df94f5868d162a6ff5892f1a2d228744cd9 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Feb 2024 18:29:20 +0100 Subject: [PATCH 1/2] RenderViewport max layout cycles should depend on number of children --- .../flutter/lib/src/rendering/viewport.dart | 7 +- .../flutter/test/rendering/viewport_test.dart | 83 +++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/rendering/viewport.dart b/packages/flutter/lib/src/rendering/viewport.dart index e1fa76e479692..45d72611f4149 100644 --- a/packages/flutter/lib/src/rendering/viewport.dart +++ b/packages/flutter/lib/src/rendering/viewport.dart @@ -1385,7 +1385,7 @@ class RenderViewport extends RenderViewportBase= _maxLayoutCycles) { + if (count >= maxLayoutCycles) { assert(count != 1); throw FlutterError( 'A RenderViewport exceeded its maximum number of layout cycles.\n' diff --git a/packages/flutter/test/rendering/viewport_test.dart b/packages/flutter/test/rendering/viewport_test.dart index 7013d6dcd183a..563e87cca6d4e 100644 --- a/packages/flutter/test/rendering/viewport_test.dart +++ b/packages/flutter/test/rendering/viewport_test.dart @@ -2370,4 +2370,87 @@ void main() { ); errors.clear(); }); + + testWidgets('RenderViewport maxLayoutCycles depends on the number of children', + (WidgetTester tester) async { + Future expectFlutterError({ + required Widget widget, + required WidgetTester tester, + }) async { + final List errors = []; + 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.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; + } } From ec2cfadc00471b03b0a39680ccabd756af6daa80 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Feb 2024 18:50:12 +0100 Subject: [PATCH 2/2] formatting --- packages/flutter/test/rendering/viewport_test.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/flutter/test/rendering/viewport_test.dart b/packages/flutter/test/rendering/viewport_test.dart index 563e87cca6d4e..8f80cfc301300 100644 --- a/packages/flutter/test/rendering/viewport_test.dart +++ b/packages/flutter/test/rendering/viewport_test.dart @@ -2395,9 +2395,7 @@ void main() { child: CustomScrollView( slivers: List.generate( sliverCount, - (_) => _ScrollOffsetCorrectionSliver( - correctionsCount: correctionsCount, - )), + (_) => _ScrollOffsetCorrectionSliver(correctionsCount: correctionsCount)), ), ); }