diff --git a/packages/flutter/lib/src/rendering/animated_size.dart b/packages/flutter/lib/src/rendering/animated_size.dart index 4b703b3232..acb538e195 100644 --- a/packages/flutter/lib/src/rendering/animated_size.dart +++ b/packages/flutter/lib/src/rendering/animated_size.dart @@ -223,6 +223,38 @@ class RenderAnimatedSize extends RenderAligningShiftedBox { _hasVisualOverflow = true; } + @override + Size computeDryLayout(BoxConstraints constraints) { + if (child == null || constraints.isTight) { + return constraints.smallest; + } + + // This simplified version of performLayout only calculates the current + // size without modifying global state. See performLayout for comments + // explaining the rational behind the implementation. + final Size childSize = child!.getDryLayout(constraints); + assert(_state != null); + switch (_state) { + case RenderAnimatedSizeState.start: + return constraints.constrain(childSize); + case RenderAnimatedSizeState.stable: + if (_sizeTween.end != childSize) { + return constraints.constrain(size); + } else if (_controller.value == _controller.upperBound) { + return constraints.constrain(childSize); + } + break; + case RenderAnimatedSizeState.unstable: + case RenderAnimatedSizeState.changed: + if (_sizeTween.end != childSize) { + return constraints.constrain(childSize); + } + break; + } + + return constraints.constrain(_animatedSize!); + } + void _restartAnimation() { _lastValue = 0.0; _controller.forward(from: 0.0); diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index cba0f4eee0..98a57c24cc 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -1769,6 +1769,7 @@ abstract class RenderBox extends RenderObject { } Map? _cachedDryLayoutSizes; + bool _computingThisDryLayout = false; /// Returns the [Size] that this [RenderBox] would like to be given the /// provided [BoxConstraints]. @@ -1800,9 +1801,24 @@ abstract class RenderBox extends RenderObject { }()); if (shouldCache) { _cachedDryLayoutSizes ??= {}; - return _cachedDryLayoutSizes!.putIfAbsent(constraints, () => computeDryLayout(constraints)); + return _cachedDryLayoutSizes!.putIfAbsent(constraints, () => _computeDryLayout(constraints)); } - return computeDryLayout(constraints); + return _computeDryLayout(constraints); + } + + Size _computeDryLayout(BoxConstraints constraints) { + assert(() { + assert(!_computingThisDryLayout); + _computingThisDryLayout = true; + return true; + }()); + final Size result = computeDryLayout(constraints); + assert(() { + assert(_computingThisDryLayout); + _computingThisDryLayout = false; + return true; + }()); + return result; } /// Computes the value returned by [getDryLayout]. Do not call this @@ -1907,7 +1923,7 @@ abstract class RenderBox extends RenderObject { assert(_size._owner == this); if (RenderObject.debugActiveLayout != null) { assert( - debugDoingThisResize || debugDoingThisLayout || + debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout || (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent), 'RenderBox.size accessed beyond the scope of resize, layout, or ' 'permitted parent access. RenderBox can always access its own size, ' diff --git a/packages/flutter/test/widgets/animated_size_test.dart b/packages/flutter/test/widgets/animated_size_test.dart index 356cf0debf..e054f9ed95 100644 --- a/packages/flutter/test/widgets/animated_size_test.dart +++ b/packages/flutter/test/widgets/animated_size_test.dart @@ -315,5 +315,53 @@ void main() { expect(renderObject.clipBehavior, clip); } }); + + testWidgets('works wrapped in IntrinsicHeight and Wrap', (WidgetTester tester) async { + Future pumpWidget(Size size, [Duration? duration]) async { + return tester.pumpWidget( + Center( + child: IntrinsicHeight( + child: Wrap( + textDirection: TextDirection.ltr, + children: [ + AnimatedSize( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutBack, + vsync: tester, + child: SizedBox( + width: size.width, + height: size.height, + ), + ), + ], + ), + ), + ), + duration, + ); + } + + await pumpWidget(const Size(100, 100)); + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(100, 100)); + + await pumpWidget(const Size(150, 200)); + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(100, 100)); + + // Each pump triggers verification of dry layout. + for (int total = 0; total < 200; total += 10) { + await tester.pump(const Duration(milliseconds: 10)); + } + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(150, 200)); + + // Change every pump + await pumpWidget(const Size(100, 100)); + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(150, 200)); + + await pumpWidget(const Size(111, 111), const Duration(milliseconds: 10)); + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(111, 111)); + + await pumpWidget(const Size(222, 222), const Duration(milliseconds: 10)); + expect(tester.renderObject(find.byType(IntrinsicHeight)).size, const Size(222, 222)); + }); }); }