@@ -17,6 +17,18 @@ typedef LayoutWidgetBuilder = Widget Function(BuildContext context, BoxConstrain
1717/// function at layout time and provides the constraints that this widget should
1818/// adhere to. This is useful when the parent constrains the child's size and layout,
1919/// and doesn't depend on the child's intrinsic size.
20+ ///
21+ /// {@template flutter.widgets.layoutBuilder.builderFunctionInvocation}
22+ /// The [builder] function is called in the following situations:
23+ ///
24+ /// * The first time the widget is laid out.
25+ /// * When the parent widget passes different layout constraints.
26+ /// * When the parent widget updates this widget.
27+ /// * When the depedencies that the [builder] function subscribes to change.
28+ ///
29+ /// The [builder] function is _not_ called during layout if the parent passes
30+ /// the same constraints repeatedly.
31+ /// {@endtemplate}
2032abstract class ConstrainedLayoutBuilder <ConstraintType extends Constraints > extends RenderObjectWidget {
2133 /// Creates a widget that defers its building until layout.
2234 ///
@@ -74,15 +86,22 @@ class _LayoutBuilderElement<ConstraintType extends Constraints> extends RenderOb
7486 assert (widget != newWidget);
7587 super .update (newWidget);
7688 assert (widget == newWidget);
89+
7790 renderObject.updateCallback (_layout);
78- renderObject.markNeedsLayout ();
91+ // Force the callback to be called, even if the layout constraints are the
92+ // same, because the logic in the callback might have changed.
93+ renderObject.markNeedsBuild ();
7994 }
8095
8196 @override
8297 void performRebuild () {
8398 // This gets called if markNeedsBuild() is called on us.
8499 // That might happen if, e.g., our builder uses Inherited widgets.
85- renderObject.markNeedsLayout ();
100+
101+ // Force the callback to be called, even if the layout constraints are the
102+ // same. This is because that callback may depend on the updated widget
103+ // configuration, or an inherited widget.
104+ renderObject.markNeedsBuild ();
86105 super .performRebuild (); // Calls widget.updateRenderObject (a no-op in this case).
87106 }
88107
@@ -168,10 +187,42 @@ mixin RenderConstrainedLayoutBuilder<ConstraintType extends Constraints, ChildTy
168187 markNeedsLayout ();
169188 }
170189
171- /// Invoke the layout callback.
172- void layoutAndBuildChild () {
190+ bool _needsBuild = true ;
191+
192+ /// Marks this layout builder as needing to rebuild.
193+ ///
194+ /// The layout build rebuilds automatically when layout constraints change.
195+ /// However, we must also rebuild when the widget updates, e.g. after
196+ /// [State.setState] , or [State.didChangeDependencies] , even when the layout
197+ /// constraints remain unchanged.
198+ ///
199+ /// See also:
200+ ///
201+ /// * [ConstrainedLayoutBuilder.builder] , which is called during the rebuild.
202+ void markNeedsBuild () {
203+ // Do not call the callback directly. It must be called during the layout
204+ // phase, when parent constraints are available. Calling `markNeedsLayout`
205+ // will cause it to be called at the right time.
206+ _needsBuild = true ;
207+ markNeedsLayout ();
208+ }
209+
210+ // The constraints that were passed to this class last time it was laid out.
211+ // These constraints are compared to the new constraints to determine whether
212+ // [ConstrainedLayoutBuilder.builder] needs to be called.
213+ Constraints _previousConstraints;
214+
215+ /// Invoke the callback supplied via [updateCallback] .
216+ ///
217+ /// Typically this results in [ConstrainedLayoutBuilder.builder] being called
218+ /// during layout.
219+ void rebuildIfNecessary () {
173220 assert (_callback != null );
174- invokeLayoutCallback (_callback);
221+ if (_needsBuild || constraints != _previousConstraints) {
222+ _previousConstraints = constraints;
223+ _needsBuild = false ;
224+ invokeLayoutCallback (_callback);
225+ }
175226 }
176227}
177228
@@ -183,11 +234,13 @@ mixin RenderConstrainedLayoutBuilder<ConstraintType extends Constraints, ChildTy
183234/// the child's intrinsic size. The [LayoutBuilder] 's final size will match its
184235/// child's size.
185236///
237+ /// {@macro flutter.widgets.layoutBuilder.builderFunctionInvocation}
238+ ///
186239/// {@youtube 560 315 https://www.youtube.com/watch?v=IYDVcriKjsw}
187240///
188241/// If the child should be smaller than the parent, consider wrapping the child
189242/// in an [Align] widget. If the child might want to be bigger, consider
190- /// wrapping it in a [SingleChildScrollView] .
243+ /// wrapping it in a [SingleChildScrollView] or [OverflowBox] .
191244///
192245/// See also:
193246///
@@ -241,7 +294,7 @@ class _RenderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin<Ren
241294 @override
242295 void performLayout () {
243296 final BoxConstraints constraints = this .constraints;
244- layoutAndBuildChild ();
297+ rebuildIfNecessary ();
245298 if (child != null ) {
246299 child.layout (constraints, parentUsesSize: true );
247300 size = constraints.constrain (child.size);
0 commit comments