1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:math' as math;
6import 'dart:ui' as ui show lerpDouble;
7
8import 'package:flutter/foundation.dart';
9import 'package:flutter/gestures.dart';
10
11import 'package:vector_math/vector_math_64.dart';
12
13import 'debug.dart';
14import 'object.dart';
15
16// Examples can assume:
17// abstract class RenderBar extends RenderBox { }
18// late RenderBox firstChild;
19// void markNeedsLayout() { }
20
21// This class should only be used in debug builds.
22class _DebugSize extends Size {
23 _DebugSize(super.source, this._owner, this._canBeUsedByParent) : super.copy();
24 final RenderBox _owner;
25 final bool _canBeUsedByParent;
26}
27
28/// Immutable layout constraints for [RenderBox] layout.
29///
30/// A [Size] respects a [BoxConstraints] if, and only if, all of the following
31/// relations hold:
32///
33/// * [minWidth] <= [Size.width] <= [maxWidth]
34/// * [minHeight] <= [Size.height] <= [maxHeight]
35///
36/// The constraints themselves must satisfy these relations:
37///
38/// * 0.0 <= [minWidth] <= [maxWidth] <= [double.infinity]
39/// * 0.0 <= [minHeight] <= [maxHeight] <= [double.infinity]
40///
41/// [double.infinity] is a legal value for each constraint.
42///
43/// ## The box layout model
44///
45/// Render objects in the Flutter framework are laid out by a one-pass layout
46/// model which walks down the render tree passing constraints, then walks back
47/// up the render tree passing concrete geometry.
48///
49/// For boxes, the constraints are [BoxConstraints], which, as described herein,
50/// consist of four numbers: a minimum width [minWidth], a maximum width
51/// [maxWidth], a minimum height [minHeight], and a maximum height [maxHeight].
52///
53/// The geometry for boxes consists of a [Size], which must satisfy the
54/// constraints described above.
55///
56/// Each [RenderBox] (the objects that provide the layout models for box
57/// widgets) receives [BoxConstraints] from its parent, then lays out each of
58/// its children, then picks a [Size] that satisfies the [BoxConstraints].
59///
60/// Render objects position their children independently of laying them out.
61/// Frequently, the parent will use the children's sizes to determine their
62/// position. A child does not know its position and will not necessarily be
63/// laid out again, or repainted, if its position changes.
64///
65/// ## Terminology
66///
67/// When the minimum constraints and the maximum constraint in an axis are the
68/// same, that axis is _tightly_ constrained. See: [
69/// BoxConstraints.tightFor], [BoxConstraints.tightForFinite], [tighten],
70/// [hasTightWidth], [hasTightHeight], [isTight].
71///
72/// An axis with a minimum constraint of 0.0 is _loose_ (regardless of the
73/// maximum constraint; if it is also 0.0, then the axis is simultaneously tight
74/// and loose!). See: [BoxConstraints.loose], [loosen].
75///
76/// An axis whose maximum constraint is not infinite is _bounded_. See:
77/// [hasBoundedWidth], [hasBoundedHeight].
78///
79/// An axis whose maximum constraint is infinite is _unbounded_. An axis is
80/// _expanding_ if it is tightly infinite (its minimum and maximum constraints
81/// are both infinite). See: [BoxConstraints.expand].
82///
83/// An axis whose _minimum_ constraint is infinite is just said to be _infinite_
84/// (since by definition the maximum constraint must also be infinite in that
85/// case). See: [hasInfiniteWidth], [hasInfiniteHeight].
86///
87/// A size is _constrained_ when it satisfies a [BoxConstraints] description.
88/// See: [constrain], [constrainWidth], [constrainHeight],
89/// [constrainDimensions], [constrainSizeAndAttemptToPreserveAspectRatio],
90/// [isSatisfiedBy].
91class BoxConstraints extends Constraints {
92 /// Creates box constraints with the given constraints.
93 const BoxConstraints({
94 this.minWidth = 0.0,
95 this.maxWidth = double.infinity,
96 this.minHeight = 0.0,
97 this.maxHeight = double.infinity,
98 });
99
100 /// Creates box constraints that is respected only by the given size.
101 BoxConstraints.tight(Size size)
102 : minWidth = size.width,
103 maxWidth = size.width,
104 minHeight = size.height,
105 maxHeight = size.height;
106
107 /// Creates box constraints that require the given width or height.
108 ///
109 /// See also:
110 ///
111 /// * [BoxConstraints.tightForFinite], which is similar but instead of
112 /// being tight if the value is non-null, is tight if the value is not
113 /// infinite.
114 const BoxConstraints.tightFor({
115 double? width,
116 double? height,
117 }) : minWidth = width ?? 0.0,
118 maxWidth = width ?? double.infinity,
119 minHeight = height ?? 0.0,
120 maxHeight = height ?? double.infinity;
121
122 /// Creates box constraints that require the given width or height, except if
123 /// they are infinite.
124 ///
125 /// See also:
126 ///
127 /// * [BoxConstraints.tightFor], which is similar but instead of being
128 /// tight if the value is not infinite, is tight if the value is non-null.
129 const BoxConstraints.tightForFinite({
130 double width = double.infinity,
131 double height = double.infinity,
132 }) : minWidth = width != double.infinity ? width : 0.0,
133 maxWidth = width != double.infinity ? width : double.infinity,
134 minHeight = height != double.infinity ? height : 0.0,
135 maxHeight = height != double.infinity ? height : double.infinity;
136
137 /// Creates box constraints that forbid sizes larger than the given size.
138 BoxConstraints.loose(Size size)
139 : minWidth = 0.0,
140 maxWidth = size.width,
141 minHeight = 0.0,
142 maxHeight = size.height;
143
144 /// Creates box constraints that expand to fill another box constraints.
145 ///
146 /// If width or height is given, the constraints will require exactly the
147 /// given value in the given dimension.
148 const BoxConstraints.expand({
149 double? width,
150 double? height,
151 }) : minWidth = width ?? double.infinity,
152 maxWidth = width ?? double.infinity,
153 minHeight = height ?? double.infinity,
154 maxHeight = height ?? double.infinity;
155
156 /// The minimum width that satisfies the constraints.
157 final double minWidth;
158
159 /// The maximum width that satisfies the constraints.
160 ///
161 /// Might be [double.infinity].
162 final double maxWidth;
163
164 /// The minimum height that satisfies the constraints.
165 final double minHeight;
166
167 /// The maximum height that satisfies the constraints.
168 ///
169 /// Might be [double.infinity].
170 final double maxHeight;
171
172 /// Creates a copy of this box constraints but with the given fields replaced with the new values.
173 BoxConstraints copyWith({
174 double? minWidth,
175 double? maxWidth,
176 double? minHeight,
177 double? maxHeight,
178 }) {
179 return BoxConstraints(
180 minWidth: minWidth ?? this.minWidth,
181 maxWidth: maxWidth ?? this.maxWidth,
182 minHeight: minHeight ?? this.minHeight,
183 maxHeight: maxHeight ?? this.maxHeight,
184 );
185 }
186
187 /// Returns new box constraints that are smaller by the given edge dimensions.
188 BoxConstraints deflate(EdgeInsets edges) {
189 assert(debugAssertIsValid());
190 final double horizontal = edges.horizontal;
191 final double vertical = edges.vertical;
192 final double deflatedMinWidth = math.max(0.0, minWidth - horizontal);
193 final double deflatedMinHeight = math.max(0.0, minHeight - vertical);
194 return BoxConstraints(
195 minWidth: deflatedMinWidth,
196 maxWidth: math.max(deflatedMinWidth, maxWidth - horizontal),
197 minHeight: deflatedMinHeight,
198 maxHeight: math.max(deflatedMinHeight, maxHeight - vertical),
199 );
200 }
201
202 /// Returns new box constraints that remove the minimum width and height requirements.
203 BoxConstraints loosen() {
204 assert(debugAssertIsValid());
205 return BoxConstraints(
206 maxWidth: maxWidth,
207 maxHeight: maxHeight,
208 );
209 }
210
211 /// Returns new box constraints that respect the given constraints while being
212 /// as close as possible to the original constraints.
213 BoxConstraints enforce(BoxConstraints constraints) {
214 return BoxConstraints(
215 minWidth: clampDouble(minWidth, constraints.minWidth, constraints.maxWidth),
216 maxWidth: clampDouble(maxWidth, constraints.minWidth, constraints.maxWidth),
217 minHeight: clampDouble(minHeight, constraints.minHeight, constraints.maxHeight),
218 maxHeight: clampDouble(maxHeight, constraints.minHeight, constraints.maxHeight),
219 );
220 }
221
222 /// Returns new box constraints with a tight width and/or height as close to
223 /// the given width and height as possible while still respecting the original
224 /// box constraints.
225 BoxConstraints tighten({ double? width, double? height }) {
226 return BoxConstraints(
227 minWidth: width == null ? minWidth : clampDouble(width, minWidth, maxWidth),
228 maxWidth: width == null ? maxWidth : clampDouble(width, minWidth, maxWidth),
229 minHeight: height == null ? minHeight : clampDouble(height, minHeight, maxHeight),
230 maxHeight: height == null ? maxHeight : clampDouble(height, minHeight, maxHeight),
231 );
232 }
233
234 /// A box constraints with the width and height constraints flipped.
235 BoxConstraints get flipped {
236 return BoxConstraints(
237 minWidth: minHeight,
238 maxWidth: maxHeight,
239 minHeight: minWidth,
240 maxHeight: maxWidth,
241 );
242 }
243
244 /// Returns box constraints with the same width constraints but with
245 /// unconstrained height.
246 BoxConstraints widthConstraints() => BoxConstraints(minWidth: minWidth, maxWidth: maxWidth);
247
248 /// Returns box constraints with the same height constraints but with
249 /// unconstrained width.
250 BoxConstraints heightConstraints() => BoxConstraints(minHeight: minHeight, maxHeight: maxHeight);
251
252 /// Returns the width that both satisfies the constraints and is as close as
253 /// possible to the given width.
254 double constrainWidth([ double width = double.infinity ]) {
255 assert(debugAssertIsValid());
256 return clampDouble(width, minWidth, maxWidth);
257 }
258
259 /// Returns the height that both satisfies the constraints and is as close as
260 /// possible to the given height.
261 double constrainHeight([ double height = double.infinity ]) {
262 assert(debugAssertIsValid());
263 return clampDouble(height, minHeight, maxHeight);
264 }
265
266 Size _debugPropagateDebugSize(Size size, Size result) {
267 assert(() {
268 if (size is _DebugSize) {
269 result = _DebugSize(result, size._owner, size._canBeUsedByParent);
270 }
271 return true;
272 }());
273 return result;
274 }
275
276 /// Returns the size that both satisfies the constraints and is as close as
277 /// possible to the given size.
278 ///
279 /// See also:
280 ///
281 /// * [constrainDimensions], which applies the same algorithm to
282 /// separately provided widths and heights.
283 Size constrain(Size size) {
284 Size result = Size(constrainWidth(size.width), constrainHeight(size.height));
285 assert(() {
286 result = _debugPropagateDebugSize(size, result);
287 return true;
288 }());
289 return result;
290 }
291
292 /// Returns the size that both satisfies the constraints and is as close as
293 /// possible to the given width and height.
294 ///
295 /// When you already have a [Size], prefer [constrain], which applies the same
296 /// algorithm to a [Size] directly.
297 Size constrainDimensions(double width, double height) {
298 return Size(constrainWidth(width), constrainHeight(height));
299 }
300
301 /// Returns a size that attempts to meet the following conditions, in order:
302 ///
303 /// * The size must satisfy these constraints.
304 /// * The aspect ratio of the returned size matches the aspect ratio of the
305 /// given size.
306 /// * The returned size is as big as possible while still being equal to or
307 /// smaller than the given size.
308 Size constrainSizeAndAttemptToPreserveAspectRatio(Size size) {
309 if (isTight) {
310 Size result = smallest;
311 assert(() {
312 result = _debugPropagateDebugSize(size, result);
313 return true;
314 }());
315 return result;
316 }
317
318 double width = size.width;
319 double height = size.height;
320 assert(width > 0.0);
321 assert(height > 0.0);
322 final double aspectRatio = width / height;
323
324 if (width > maxWidth) {
325 width = maxWidth;
326 height = width / aspectRatio;
327 }
328
329 if (height > maxHeight) {
330 height = maxHeight;
331 width = height * aspectRatio;
332 }
333
334 if (width < minWidth) {
335 width = minWidth;
336 height = width / aspectRatio;
337 }
338
339 if (height < minHeight) {
340 height = minHeight;
341 width = height * aspectRatio;
342 }
343
344 Size result = Size(constrainWidth(width), constrainHeight(height));
345 assert(() {
346 result = _debugPropagateDebugSize(size, result);
347 return true;
348 }());
349 return result;
350 }
351
352 /// The biggest size that satisfies the constraints.
353 Size get biggest => Size(constrainWidth(), constrainHeight());
354
355 /// The smallest size that satisfies the constraints.
356 Size get smallest => Size(constrainWidth(0.0), constrainHeight(0.0));
357
358 /// Whether there is exactly one width value that satisfies the constraints.
359 bool get hasTightWidth => minWidth >= maxWidth;
360
361 /// Whether there is exactly one height value that satisfies the constraints.
362 bool get hasTightHeight => minHeight >= maxHeight;
363
364 /// Whether there is exactly one size that satisfies the constraints.
365 @override
366 bool get isTight => hasTightWidth && hasTightHeight;
367
368 /// Whether there is an upper bound on the maximum width.
369 ///
370 /// See also:
371 ///
372 /// * [hasBoundedHeight], the equivalent for the vertical axis.
373 /// * [hasInfiniteWidth], which describes whether the minimum width
374 /// constraint is infinite.
375 bool get hasBoundedWidth => maxWidth < double.infinity;
376
377 /// Whether there is an upper bound on the maximum height.
378 ///
379 /// See also:
380 ///
381 /// * [hasBoundedWidth], the equivalent for the horizontal axis.
382 /// * [hasInfiniteHeight], which describes whether the minimum height
383 /// constraint is infinite.
384 bool get hasBoundedHeight => maxHeight < double.infinity;
385
386 /// Whether the width constraint is infinite.
387 ///
388 /// Such a constraint is used to indicate that a box should grow as large as
389 /// some other constraint (in this case, horizontally). If constraints are
390 /// infinite, then they must have other (non-infinite) constraints [enforce]d
391 /// upon them, or must be [tighten]ed, before they can be used to derive a
392 /// [Size] for a [RenderBox.size].
393 ///
394 /// See also:
395 ///
396 /// * [hasInfiniteHeight], the equivalent for the vertical axis.
397 /// * [hasBoundedWidth], which describes whether the maximum width
398 /// constraint is finite.
399 bool get hasInfiniteWidth => minWidth >= double.infinity;
400
401 /// Whether the height constraint is infinite.
402 ///
403 /// Such a constraint is used to indicate that a box should grow as large as
404 /// some other constraint (in this case, vertically). If constraints are
405 /// infinite, then they must have other (non-infinite) constraints [enforce]d
406 /// upon them, or must be [tighten]ed, before they can be used to derive a
407 /// [Size] for a [RenderBox.size].
408 ///
409 /// See also:
410 ///
411 /// * [hasInfiniteWidth], the equivalent for the horizontal axis.
412 /// * [hasBoundedHeight], which describes whether the maximum height
413 /// constraint is finite.
414 bool get hasInfiniteHeight => minHeight >= double.infinity;
415
416 /// Whether the given size satisfies the constraints.
417 bool isSatisfiedBy(Size size) {
418 assert(debugAssertIsValid());
419 return (minWidth <= size.width) && (size.width <= maxWidth) &&
420 (minHeight <= size.height) && (size.height <= maxHeight);
421 }
422
423 /// Scales each constraint parameter by the given factor.
424 BoxConstraints operator*(double factor) {
425 return BoxConstraints(
426 minWidth: minWidth * factor,
427 maxWidth: maxWidth * factor,
428 minHeight: minHeight * factor,
429 maxHeight: maxHeight * factor,
430 );
431 }
432
433 /// Scales each constraint parameter by the inverse of the given factor.
434 BoxConstraints operator/(double factor) {
435 return BoxConstraints(
436 minWidth: minWidth / factor,
437 maxWidth: maxWidth / factor,
438 minHeight: minHeight / factor,
439 maxHeight: maxHeight / factor,
440 );
441 }
442
443 /// Scales each constraint parameter by the inverse of the given factor, rounded to the nearest integer.
444 BoxConstraints operator~/(double factor) {
445 return BoxConstraints(
446 minWidth: (minWidth ~/ factor).toDouble(),
447 maxWidth: (maxWidth ~/ factor).toDouble(),
448 minHeight: (minHeight ~/ factor).toDouble(),
449 maxHeight: (maxHeight ~/ factor).toDouble(),
450 );
451 }
452
453 /// Computes the remainder of each constraint parameter by the given value.
454 BoxConstraints operator%(double value) {
455 return BoxConstraints(
456 minWidth: minWidth % value,
457 maxWidth: maxWidth % value,
458 minHeight: minHeight % value,
459 maxHeight: maxHeight % value,
460 );
461 }
462
463 /// Linearly interpolate between two BoxConstraints.
464 ///
465 /// If either is null, this function interpolates from a [BoxConstraints]
466 /// object whose fields are all set to 0.0.
467 ///
468 /// {@macro dart.ui.shadow.lerp}
469 static BoxConstraints? lerp(BoxConstraints? a, BoxConstraints? b, double t) {
470 if (identical(a, b)) {
471 return a;
472 }
473 if (a == null) {
474 return b! * t;
475 }
476 if (b == null) {
477 return a * (1.0 - t);
478 }
479 assert(a.debugAssertIsValid());
480 assert(b.debugAssertIsValid());
481 assert((a.minWidth.isFinite && b.minWidth.isFinite) || (a.minWidth == double.infinity && b.minWidth == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
482 assert((a.maxWidth.isFinite && b.maxWidth.isFinite) || (a.maxWidth == double.infinity && b.maxWidth == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
483 assert((a.minHeight.isFinite && b.minHeight.isFinite) || (a.minHeight == double.infinity && b.minHeight == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
484 assert((a.maxHeight.isFinite && b.maxHeight.isFinite) || (a.maxHeight == double.infinity && b.maxHeight == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
485 return BoxConstraints(
486 minWidth: a.minWidth.isFinite ? ui.lerpDouble(a.minWidth, b.minWidth, t)! : double.infinity,
487 maxWidth: a.maxWidth.isFinite ? ui.lerpDouble(a.maxWidth, b.maxWidth, t)! : double.infinity,
488 minHeight: a.minHeight.isFinite ? ui.lerpDouble(a.minHeight, b.minHeight, t)! : double.infinity,
489 maxHeight: a.maxHeight.isFinite ? ui.lerpDouble(a.maxHeight, b.maxHeight, t)! : double.infinity,
490 );
491 }
492
493 /// Returns whether the object's constraints are normalized.
494 /// Constraints are normalized if the minimums are less than or
495 /// equal to the corresponding maximums.
496 ///
497 /// For example, a BoxConstraints object with a minWidth of 100.0
498 /// and a maxWidth of 90.0 is not normalized.
499 ///
500 /// Most of the APIs on BoxConstraints expect the constraints to be
501 /// normalized and have undefined behavior when they are not. In
502 /// debug mode, many of these APIs will assert if the constraints
503 /// are not normalized.
504 @override
505 bool get isNormalized {
506 return minWidth >= 0.0 &&
507 minWidth <= maxWidth &&
508 minHeight >= 0.0 &&
509 minHeight <= maxHeight;
510 }
511
512 @override
513 bool debugAssertIsValid({
514 bool isAppliedConstraint = false,
515 InformationCollector? informationCollector,
516 }) {
517 assert(() {
518 void throwError(DiagnosticsNode message) {
519 throw FlutterError.fromParts(<DiagnosticsNode>[
520 message,
521 if (informationCollector != null) ...informationCollector(),
522 DiagnosticsProperty<BoxConstraints>('The offending constraints were', this, style: DiagnosticsTreeStyle.errorProperty),
523 ]);
524 }
525 if (minWidth.isNaN || maxWidth.isNaN || minHeight.isNaN || maxHeight.isNaN) {
526 final List<String> affectedFieldsList = <String>[
527 if (minWidth.isNaN) 'minWidth',
528 if (maxWidth.isNaN) 'maxWidth',
529 if (minHeight.isNaN) 'minHeight',
530 if (maxHeight.isNaN) 'maxHeight',
531 ];
532 assert(affectedFieldsList.isNotEmpty);
533 if (affectedFieldsList.length > 1) {
534 affectedFieldsList.add('and ${affectedFieldsList.removeLast()}');
535 }
536 String whichFields = '';
537 if (affectedFieldsList.length > 2) {
538 whichFields = affectedFieldsList.join(', ');
539 } else if (affectedFieldsList.length == 2) {
540 whichFields = affectedFieldsList.join(' ');
541 } else {
542 whichFields = affectedFieldsList.single;
543 }
544 throwError(ErrorSummary('BoxConstraints has ${affectedFieldsList.length == 1 ? 'a NaN value' : 'NaN values' } in $whichFields.'));
545 }
546 if (minWidth < 0.0 && minHeight < 0.0) {
547 throwError(ErrorSummary('BoxConstraints has both a negative minimum width and a negative minimum height.'));
548 }
549 if (minWidth < 0.0) {
550 throwError(ErrorSummary('BoxConstraints has a negative minimum width.'));
551 }
552 if (minHeight < 0.0) {
553 throwError(ErrorSummary('BoxConstraints has a negative minimum height.'));
554 }
555 if (maxWidth < minWidth && maxHeight < minHeight) {
556 throwError(ErrorSummary('BoxConstraints has both width and height constraints non-normalized.'));
557 }
558 if (maxWidth < minWidth) {
559 throwError(ErrorSummary('BoxConstraints has non-normalized width constraints.'));
560 }
561 if (maxHeight < minHeight) {
562 throwError(ErrorSummary('BoxConstraints has non-normalized height constraints.'));
563 }
564 if (isAppliedConstraint) {
565 if (minWidth.isInfinite && minHeight.isInfinite) {
566 throwError(ErrorSummary('BoxConstraints forces an infinite width and infinite height.'));
567 }
568 if (minWidth.isInfinite) {
569 throwError(ErrorSummary('BoxConstraints forces an infinite width.'));
570 }
571 if (minHeight.isInfinite) {
572 throwError(ErrorSummary('BoxConstraints forces an infinite height.'));
573 }
574 }
575 assert(isNormalized);
576 return true;
577 }());
578 return isNormalized;
579 }
580
581 /// Returns a box constraints that [isNormalized].
582 ///
583 /// The returned [maxWidth] is at least as large as the [minWidth]. Similarly,
584 /// the returned [maxHeight] is at least as large as the [minHeight].
585 BoxConstraints normalize() {
586 if (isNormalized) {
587 return this;
588 }
589 final double minWidth = this.minWidth >= 0.0 ? this.minWidth : 0.0;
590 final double minHeight = this.minHeight >= 0.0 ? this.minHeight : 0.0;
591 return BoxConstraints(
592 minWidth: minWidth,
593 maxWidth: minWidth > maxWidth ? minWidth : maxWidth,
594 minHeight: minHeight,
595 maxHeight: minHeight > maxHeight ? minHeight : maxHeight,
596 );
597 }
598
599 @override
600 bool operator ==(Object other) {
601 assert(debugAssertIsValid());
602 if (identical(this, other)) {
603 return true;
604 }
605 if (other.runtimeType != runtimeType) {
606 return false;
607 }
608 assert(other is BoxConstraints && other.debugAssertIsValid());
609 return other is BoxConstraints
610 && other.minWidth == minWidth
611 && other.maxWidth == maxWidth
612 && other.minHeight == minHeight
613 && other.maxHeight == maxHeight;
614 }
615
616 @override
617 int get hashCode {
618 assert(debugAssertIsValid());
619 return Object.hash(minWidth, maxWidth, minHeight, maxHeight);
620 }
621
622 @override
623 String toString() {
624 final String annotation = isNormalized ? '' : '; NOT NORMALIZED';
625 if (minWidth == double.infinity && minHeight == double.infinity) {
626 return 'BoxConstraints(biggest$annotation)';
627 }
628 if (minWidth == 0 && maxWidth == double.infinity &&
629 minHeight == 0 && maxHeight == double.infinity) {
630 return 'BoxConstraints(unconstrained$annotation)';
631 }
632 String describe(double min, double max, String dim) {
633 if (min == max) {
634 return '$dim=${min.toStringAsFixed(1)}';
635 }
636 return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}';
637 }
638 final String width = describe(minWidth, maxWidth, 'w');
639 final String height = describe(minHeight, maxHeight, 'h');
640 return 'BoxConstraints($width, $height$annotation)';
641 }
642}
643
644/// Method signature for hit testing a [RenderBox].
645///
646/// Used by [BoxHitTestResult.addWithPaintTransform] to hit test children
647/// of a [RenderBox].
648///
649/// See also:
650///
651/// * [RenderBox.hitTest], which documents more details around hit testing
652/// [RenderBox]es.
653typedef BoxHitTest = bool Function(BoxHitTestResult result, Offset position);
654
655/// Method signature for hit testing a [RenderBox] with a manually
656/// managed position (one that is passed out-of-band).
657///
658/// Used by [RenderSliverSingleBoxAdapter.hitTestBoxChild] to hit test
659/// [RenderBox] children of a [RenderSliver].
660///
661/// See also:
662///
663/// * [RenderBox.hitTest], which documents more details around hit testing
664/// [RenderBox]es.
665typedef BoxHitTestWithOutOfBandPosition = bool Function(BoxHitTestResult result);
666
667/// The result of performing a hit test on [RenderBox]es.
668///
669/// An instance of this class is provided to [RenderBox.hitTest] to record the
670/// result of the hit test.
671class BoxHitTestResult extends HitTestResult {
672 /// Creates an empty hit test result for hit testing on [RenderBox].
673 BoxHitTestResult() : super();
674
675 /// Wraps `result` to create a [HitTestResult] that implements the
676 /// [BoxHitTestResult] protocol for hit testing on [RenderBox]es.
677 ///
678 /// This method is used by [RenderObject]s that adapt between the
679 /// [RenderBox]-world and the non-[RenderBox]-world to convert a (subtype of)
680 /// [HitTestResult] to a [BoxHitTestResult] for hit testing on [RenderBox]es.
681 ///
682 /// The [HitTestEntry] instances added to the returned [BoxHitTestResult] are
683 /// also added to the wrapped `result` (both share the same underlying data
684 /// structure to store [HitTestEntry] instances).
685 ///
686 /// See also:
687 ///
688 /// * [HitTestResult.wrap], which turns a [BoxHitTestResult] back into a
689 /// generic [HitTestResult].
690 /// * [SliverHitTestResult.wrap], which turns a [BoxHitTestResult] into a
691 /// [SliverHitTestResult] for hit testing on [RenderSliver] children.
692 BoxHitTestResult.wrap(super.result) : super.wrap();
693
694 /// Transforms `position` to the local coordinate system of a child for
695 /// hit-testing the child.
696 ///
697 /// The actual hit testing of the child needs to be implemented in the
698 /// provided `hitTest` callback, which is invoked with the transformed
699 /// `position` as argument.
700 ///
701 /// The provided paint `transform` (which describes the transform from the
702 /// child to the parent in 3D) is processed by
703 /// [PointerEvent.removePerspectiveTransform] to remove the
704 /// perspective component and inverted before it is used to transform
705 /// `position` from the coordinate system of the parent to the system of the
706 /// child.
707 ///
708 /// If `transform` is null it will be treated as the identity transform and
709 /// `position` is provided to the `hitTest` callback as-is. If `transform`
710 /// cannot be inverted, the `hitTest` callback is not invoked and false is
711 /// returned. Otherwise, the return value of the `hitTest` callback is
712 /// returned.
713 ///
714 /// The `position` argument may be null, which will be forwarded to the
715 /// `hitTest` callback as-is. Using null as the position can be useful if
716 /// the child speaks a different hit test protocol than the parent and the
717 /// position is not required to do the actual hit testing in that protocol.
718 ///
719 /// The function returns the return value of the `hitTest` callback.
720 ///
721 /// {@tool snippet}
722 /// This method is used in [RenderBox.hitTestChildren] when the child and
723 /// parent don't share the same origin.
724 ///
725 /// ```dart
726 /// abstract class RenderFoo extends RenderBox {
727 /// final Matrix4 _effectiveTransform = Matrix4.rotationZ(50);
728 ///
729 /// @override
730 /// void applyPaintTransform(RenderBox child, Matrix4 transform) {
731 /// transform.multiply(_effectiveTransform);
732 /// }
733 ///
734 /// @override
735 /// bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
736 /// return result.addWithPaintTransform(
737 /// transform: _effectiveTransform,
738 /// position: position,
739 /// hitTest: (BoxHitTestResult result, Offset position) {
740 /// return super.hitTestChildren(result, position: position);
741 /// },
742 /// );
743 /// }
744 /// }
745 /// ```
746 /// {@end-tool}
747 ///
748 /// See also:
749 ///
750 /// * [addWithPaintOffset], which can be used for `transform`s that are just
751 /// simple matrix translations by an [Offset].
752 /// * [addWithRawTransform], which takes a transform matrix that is directly
753 /// used to transform the position without any pre-processing.
754 bool addWithPaintTransform({
755 required Matrix4? transform,
756 required Offset position,
757 required BoxHitTest hitTest,
758 }) {
759 if (transform != null) {
760 transform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(transform));
761 if (transform == null) {
762 // Objects are not visible on screen and cannot be hit-tested.
763 return false;
764 }
765 }
766 return addWithRawTransform(
767 transform: transform,
768 position: position,
769 hitTest: hitTest,
770 );
771 }
772
773 /// Convenience method for hit testing children, that are translated by
774 /// an [Offset].
775 ///
776 /// The actual hit testing of the child needs to be implemented in the
777 /// provided `hitTest` callback, which is invoked with the transformed
778 /// `position` as argument.
779 ///
780 /// This method can be used as a convenience over [addWithPaintTransform] if
781 /// a parent paints a child at an `offset`.
782 ///
783 /// A null value for `offset` is treated as if [Offset.zero] was provided.
784 ///
785 /// The function returns the return value of the `hitTest` callback.
786 ///
787 /// See also:
788 ///
789 /// * [addWithPaintTransform], which takes a generic paint transform matrix and
790 /// documents the intended usage of this API in more detail.
791 bool addWithPaintOffset({
792 required Offset? offset,
793 required Offset position,
794 required BoxHitTest hitTest,
795 }) {
796 final Offset transformedPosition = offset == null ? position : position - offset;
797 if (offset != null) {
798 pushOffset(-offset);
799 }
800 final bool isHit = hitTest(this, transformedPosition);
801 if (offset != null) {
802 popTransform();
803 }
804 return isHit;
805 }
806
807 /// Transforms `position` to the local coordinate system of a child for
808 /// hit-testing the child.
809 ///
810 /// The actual hit testing of the child needs to be implemented in the
811 /// provided `hitTest` callback, which is invoked with the transformed
812 /// `position` as argument.
813 ///
814 /// Unlike [addWithPaintTransform], the provided `transform` matrix is used
815 /// directly to transform `position` without any pre-processing.
816 ///
817 /// If `transform` is null it will be treated as the identity transform ad
818 /// `position` is provided to the `hitTest` callback as-is.
819 ///
820 /// The function returns the return value of the `hitTest` callback.
821 ///
822 /// See also:
823 ///
824 /// * [addWithPaintTransform], which accomplishes the same thing, but takes a
825 /// _paint_ transform matrix.
826 bool addWithRawTransform({
827 required Matrix4? transform,
828 required Offset position,
829 required BoxHitTest hitTest,
830 }) {
831 final Offset transformedPosition = transform == null ?
832 position : MatrixUtils.transformPoint(transform, position);
833 if (transform != null) {
834 pushTransform(transform);
835 }
836 final bool isHit = hitTest(this, transformedPosition);
837 if (transform != null) {
838 popTransform();
839 }
840 return isHit;
841 }
842
843 /// Pass-through method for adding a hit test while manually managing
844 /// the position transformation logic.
845 ///
846 /// The actual hit testing of the child needs to be implemented in the
847 /// provided `hitTest` callback. The position needs to be handled by
848 /// the caller.
849 ///
850 /// The function returns the return value of the `hitTest` callback.
851 ///
852 /// A `paintOffset`, `paintTransform`, or `rawTransform` should be
853 /// passed to the method to update the hit test stack.
854 ///
855 /// * `paintOffset` has the semantics of the `offset` passed to
856 /// [addWithPaintOffset].
857 ///
858 /// * `paintTransform` has the semantics of the `transform` passed to
859 /// [addWithPaintTransform], except that it must be invertible; it
860 /// is the responsibility of the caller to ensure this.
861 ///
862 /// * `rawTransform` has the semantics of the `transform` passed to
863 /// [addWithRawTransform].
864 ///
865 /// Exactly one of these must be non-null.
866 ///
867 /// See also:
868 ///
869 /// * [addWithPaintTransform], which takes a generic paint transform matrix and
870 /// documents the intended usage of this API in more detail.
871 bool addWithOutOfBandPosition({
872 Offset? paintOffset,
873 Matrix4? paintTransform,
874 Matrix4? rawTransform,
875 required BoxHitTestWithOutOfBandPosition hitTest,
876 }) {
877 assert(
878 (paintOffset == null && paintTransform == null && rawTransform != null) ||
879 (paintOffset == null && paintTransform != null && rawTransform == null) ||
880 (paintOffset != null && paintTransform == null && rawTransform == null),
881 'Exactly one transform or offset argument must be provided.',
882 );
883 if (paintOffset != null) {
884 pushOffset(-paintOffset);
885 } else if (rawTransform != null) {
886 pushTransform(rawTransform);
887 } else {
888 assert(paintTransform != null);
889 paintTransform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(paintTransform!));
890 assert(paintTransform != null, 'paintTransform must be invertible.');
891 pushTransform(paintTransform!);
892 }
893 final bool isHit = hitTest(this);
894 popTransform();
895 return isHit;
896 }
897}
898
899/// A hit test entry used by [RenderBox].
900class BoxHitTestEntry extends HitTestEntry<RenderBox> {
901 /// Creates a box hit test entry.
902 BoxHitTestEntry(super.target, this.localPosition);
903
904 /// The position of the hit test in the local coordinates of [target].
905 final Offset localPosition;
906
907 @override
908 String toString() => '${describeIdentity(target)}@$localPosition';
909}
910
911/// Parent data used by [RenderBox] and its subclasses.
912///
913/// {@tool dartpad}
914/// Parent data is used to communicate to a render object about its
915/// children. In this example, there are two render objects that perform
916/// text layout. They use parent data to identify the kind of child they
917/// are laying out, and space the children accordingly.
918///
919/// ** See code in examples/api/lib/rendering/box/parent_data.0.dart **
920/// {@end-tool}
921class BoxParentData extends ParentData {
922 /// The offset at which to paint the child in the parent's coordinate system.
923 Offset offset = Offset.zero;
924
925 @override
926 String toString() => 'offset=$offset';
927}
928
929/// Abstract [ParentData] subclass for [RenderBox] subclasses that want the
930/// [ContainerRenderObjectMixin].
931///
932/// This is a convenience class that mixes in the relevant classes with
933/// the relevant type arguments.
934abstract class ContainerBoxParentData<ChildType extends RenderObject> extends BoxParentData with ContainerParentDataMixin<ChildType> { }
935
936enum _IntrinsicDimension { minWidth, maxWidth, minHeight, maxHeight }
937
938@immutable
939class _IntrinsicDimensionsCacheEntry {
940 const _IntrinsicDimensionsCacheEntry(this.dimension, this.argument);
941
942 final _IntrinsicDimension dimension;
943 final double argument;
944
945 @override
946 bool operator ==(Object other) {
947 return other is _IntrinsicDimensionsCacheEntry
948 && other.dimension == dimension
949 && other.argument == argument;
950 }
951
952 @override
953 int get hashCode => Object.hash(dimension, argument);
954}
955
956/// A render object in a 2D Cartesian coordinate system.
957///
958/// The [size] of each box is expressed as a width and a height. Each box has
959/// its own coordinate system in which its upper left corner is placed at (0,
960/// 0). The lower right corner of the box is therefore at (width, height). The
961/// box contains all the points including the upper left corner and extending
962/// to, but not including, the lower right corner.
963///
964/// Box layout is performed by passing a [BoxConstraints] object down the tree.
965/// The box constraints establish a min and max value for the child's width and
966/// height. In determining its size, the child must respect the constraints
967/// given to it by its parent.
968///
969/// This protocol is sufficient for expressing a number of common box layout
970/// data flows. For example, to implement a width-in-height-out data flow, call
971/// your child's [layout] function with a set of box constraints with a tight
972/// width value (and pass true for parentUsesSize). After the child determines
973/// its height, use the child's height to determine your size.
974///
975/// ## Writing a RenderBox subclass
976///
977/// One would implement a new [RenderBox] subclass to describe a new layout
978/// model, new paint model, new hit-testing model, or new semantics model, while
979/// remaining in the Cartesian space defined by the [RenderBox] protocol.
980///
981/// To create a new protocol, consider subclassing [RenderObject] instead.
982///
983/// ### Constructors and properties of a new RenderBox subclass
984///
985/// The constructor will typically take a named argument for each property of
986/// the class. The value is then passed to a private field of the class and the
987/// constructor asserts its correctness (e.g. if it should not be null, it
988/// asserts it's not null).
989///
990/// Properties have the form of a getter/setter/field group like the following:
991///
992/// ```dart
993/// AxisDirection get axis => _axis;
994/// AxisDirection _axis = AxisDirection.down; // or initialized in constructor
995/// set axis(AxisDirection value) {
996/// if (value == _axis) {
997/// return;
998/// }
999/// _axis = value;
1000/// markNeedsLayout();
1001/// }
1002/// ```
1003///
1004/// The setter will typically finish with either a call to [markNeedsLayout], if
1005/// the layout uses this property, or [markNeedsPaint], if only the painter
1006/// function does. (No need to call both, [markNeedsLayout] implies
1007/// [markNeedsPaint].)
1008///
1009/// Consider layout and paint to be expensive; be conservative about calling
1010/// [markNeedsLayout] or [markNeedsPaint]. They should only be called if the
1011/// layout (or paint, respectively) has actually changed.
1012///
1013/// ### Children
1014///
1015/// If a render object is a leaf, that is, it cannot have any children, then
1016/// ignore this section. (Examples of leaf render objects are [RenderImage] and
1017/// [RenderParagraph].)
1018///
1019/// For render objects with children, there are four possible scenarios:
1020///
1021/// * A single [RenderBox] child. In this scenario, consider inheriting from
1022/// [RenderProxyBox] (if the render object sizes itself to match the child) or
1023/// [RenderShiftedBox] (if the child will be smaller than the box and the box
1024/// will align the child inside itself).
1025///
1026/// * A single child, but it isn't a [RenderBox]. Use the
1027/// [RenderObjectWithChildMixin] mixin.
1028///
1029/// * A single list of children. Use the [ContainerRenderObjectMixin] mixin.
1030///
1031/// * A more complicated child model.
1032///
1033/// #### Using RenderProxyBox
1034///
1035/// By default, a [RenderProxyBox] render object sizes itself to fit its child, or
1036/// to be as small as possible if there is no child; it passes all hit testing
1037/// and painting on to the child, and intrinsic dimensions and baseline
1038/// measurements similarly are proxied to the child.
1039///
1040/// A subclass of [RenderProxyBox] just needs to override the parts of the
1041/// [RenderBox] protocol that matter. For example, [RenderOpacity] just
1042/// overrides the paint method (and [alwaysNeedsCompositing] to reflect what the
1043/// paint method does, and the [visitChildrenForSemantics] method so that the
1044/// child is hidden from accessibility tools when it's invisible), and adds an
1045/// [RenderOpacity.opacity] field.
1046///
1047/// [RenderProxyBox] assumes that the child is the size of the parent and
1048/// positioned at 0,0. If this is not true, then use [RenderShiftedBox] instead.
1049///
1050/// See
1051/// [proxy_box.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/proxy_box.dart)
1052/// for examples of inheriting from [RenderProxyBox].
1053///
1054/// #### Using RenderShiftedBox
1055///
1056/// By default, a [RenderShiftedBox] acts much like a [RenderProxyBox] but
1057/// without assuming that the child is positioned at 0,0 (the actual position
1058/// recorded in the child's [parentData] field is used), and without providing a
1059/// default layout algorithm.
1060///
1061/// See
1062/// [shifted_box.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/shifted_box.dart)
1063/// for examples of inheriting from [RenderShiftedBox].
1064///
1065/// #### Kinds of children and child-specific data
1066///
1067/// A [RenderBox] doesn't have to have [RenderBox] children. One can use another
1068/// subclass of [RenderObject] for a [RenderBox]'s children. See the discussion
1069/// at [RenderObject].
1070///
1071/// Children can have additional data owned by the parent but stored on the
1072/// child using the [parentData] field. The class used for that data must
1073/// inherit from [ParentData]. The [setupParentData] method is used to
1074/// initialize the [parentData] field of a child when the child is attached.
1075///
1076/// By convention, [RenderBox] objects that have [RenderBox] children use the
1077/// [BoxParentData] class, which has a [BoxParentData.offset] field to store the
1078/// position of the child relative to the parent. ([RenderProxyBox] does not
1079/// need this offset and therefore is an exception to this rule.)
1080///
1081/// #### Using RenderObjectWithChildMixin
1082///
1083/// If a render object has a single child but it isn't a [RenderBox], then the
1084/// [RenderObjectWithChildMixin] class, which is a mixin that will handle the
1085/// boilerplate of managing a child, will be useful.
1086///
1087/// It's a generic class with one type argument, the type of the child. For
1088/// example, if you are building a `RenderFoo` class which takes a single
1089/// `RenderBar` child, you would use the mixin as follows:
1090///
1091/// ```dart
1092/// class RenderFoo extends RenderBox
1093/// with RenderObjectWithChildMixin<RenderBar> {
1094/// // ...
1095/// }
1096/// ```
1097///
1098/// Since the `RenderFoo` class itself is still a [RenderBox] in this case, you
1099/// still have to implement the [RenderBox] layout algorithm, as well as
1100/// features like intrinsics and baselines, painting, and hit testing.
1101///
1102/// #### Using ContainerRenderObjectMixin
1103///
1104/// If a render box can have multiple children, then the
1105/// [ContainerRenderObjectMixin] mixin can be used to handle the boilerplate. It
1106/// uses a linked list to model the children in a manner that is easy to mutate
1107/// dynamically and that can be walked efficiently. Random access is not
1108/// efficient in this model; if you need random access to the children consider
1109/// the next section on more complicated child models.
1110///
1111/// The [ContainerRenderObjectMixin] class has two type arguments. The first is
1112/// the type of the child objects. The second is the type for their
1113/// [parentData]. The class used for [parentData] must itself have the
1114/// [ContainerParentDataMixin] class mixed into it; this is where
1115/// [ContainerRenderObjectMixin] stores the linked list. A [ParentData] class
1116/// can extend [ContainerBoxParentData]; this is essentially
1117/// [BoxParentData] mixed with [ContainerParentDataMixin]. For example, if a
1118/// `RenderFoo` class wanted to have a linked list of [RenderBox] children, one
1119/// might create a `FooParentData` class as follows:
1120///
1121/// ```dart
1122/// class FooParentData extends ContainerBoxParentData<RenderBox> {
1123/// // (any fields you might need for these children)
1124/// }
1125/// ```
1126///
1127/// When using [ContainerRenderObjectMixin] in a [RenderBox], consider mixing in
1128/// [RenderBoxContainerDefaultsMixin], which provides a collection of utility
1129/// methods that implement common parts of the [RenderBox] protocol (such as
1130/// painting the children).
1131///
1132/// The declaration of the `RenderFoo` class itself would thus look like this:
1133///
1134/// ```dart
1135/// // continuing from previous example...
1136/// class RenderFoo extends RenderBox with
1137/// ContainerRenderObjectMixin<RenderBox, FooParentData>,
1138/// RenderBoxContainerDefaultsMixin<RenderBox, FooParentData> {
1139/// // ...
1140/// }
1141/// ```
1142///
1143/// When walking the children (e.g. during layout), the following pattern is
1144/// commonly used (in this case assuming that the children are all [RenderBox]
1145/// objects and that this render object uses `FooParentData` objects for its
1146/// children's [parentData] fields):
1147///
1148/// ```dart
1149/// // continuing from previous example...
1150/// RenderBox? child = firstChild;
1151/// while (child != null) {
1152/// final FooParentData childParentData = child.parentData! as FooParentData;
1153/// // ...operate on child and childParentData...
1154/// assert(child.parentData == childParentData);
1155/// child = childParentData.nextSibling;
1156/// }
1157/// ```
1158///
1159/// #### More complicated child models
1160///
1161/// Render objects can have more complicated models, for example a map of
1162/// children keyed on an enum, or a 2D grid of efficiently randomly-accessible
1163/// children, or multiple lists of children, etc. If a render object has a model
1164/// that can't be handled by the mixins above, it must implement the
1165/// [RenderObject] child protocol, as follows:
1166///
1167/// * Any time a child is removed, call [dropChild] with the child.
1168///
1169/// * Any time a child is added, call [adoptChild] with the child.
1170///
1171/// * Implement the [attach] method such that it calls [attach] on each child.
1172///
1173/// * Implement the [detach] method such that it calls [detach] on each child.
1174///
1175/// * Implement the [redepthChildren] method such that it calls [redepthChild]
1176/// on each child.
1177///
1178/// * Implement the [visitChildren] method such that it calls its argument for
1179/// each child, typically in paint order (back-most to front-most).
1180///
1181/// * Implement [debugDescribeChildren] such that it outputs a [DiagnosticsNode]
1182/// for each child.
1183///
1184/// Implementing these seven bullet points is essentially all that the two
1185/// aforementioned mixins do.
1186///
1187/// ### Layout
1188///
1189/// [RenderBox] classes implement a layout algorithm. They have a set of
1190/// constraints provided to them, and they size themselves based on those
1191/// constraints and whatever other inputs they may have (for example, their
1192/// children or properties).
1193///
1194/// When implementing a [RenderBox] subclass, one must make a choice. Does it
1195/// size itself exclusively based on the constraints, or does it use any other
1196/// information in sizing itself? An example of sizing purely based on the
1197/// constraints would be growing to fit the parent.
1198///
1199/// Sizing purely based on the constraints allows the system to make some
1200/// significant optimizations. Classes that use this approach should override
1201/// [sizedByParent] to return true, and then override [computeDryLayout] to
1202/// compute the [Size] using nothing but the constraints, e.g.:
1203///
1204/// ```dart
1205/// @override
1206/// bool get sizedByParent => true;
1207///
1208/// @override
1209/// Size computeDryLayout(BoxConstraints constraints) {
1210/// return constraints.smallest;
1211/// }
1212/// ```
1213///
1214/// Otherwise, the size is set in the [performLayout] function.
1215///
1216/// The [performLayout] function is where render boxes decide, if they are not
1217/// [sizedByParent], what [size] they should be, and also where they decide
1218/// where their children should be.
1219///
1220/// #### Layout of RenderBox children
1221///
1222/// The [performLayout] function should call the [layout] function of each (box)
1223/// child, passing it a [BoxConstraints] object describing the constraints
1224/// within which the child can render. Passing tight constraints (see
1225/// [BoxConstraints.isTight]) to the child will allow the rendering library to
1226/// apply some optimizations, as it knows that if the constraints are tight, the
1227/// child's dimensions cannot change even if the layout of the child itself
1228/// changes.
1229///
1230/// If the [performLayout] function will use the child's size to affect other
1231/// aspects of the layout, for example if the render box sizes itself around the
1232/// child, or positions several children based on the size of those children,
1233/// then it must specify the `parentUsesSize` argument to the child's [layout]
1234/// function, setting it to true.
1235///
1236/// This flag turns off some optimizations; algorithms that do not rely on the
1237/// children's sizes will be more efficient. (In particular, relying on the
1238/// child's [size] means that if the child is marked dirty for layout, the
1239/// parent will probably also be marked dirty for layout, unless the
1240/// [constraints] given by the parent to the child were tight constraints.)
1241///
1242/// For [RenderBox] classes that do not inherit from [RenderProxyBox], once they
1243/// have laid out their children, they should also position them, by setting the
1244/// [BoxParentData.offset] field of each child's [parentData] object.
1245///
1246/// #### Layout of non-RenderBox children
1247///
1248/// The children of a [RenderBox] do not have to be [RenderBox]es themselves. If
1249/// they use another protocol (as discussed at [RenderObject]), then instead of
1250/// [BoxConstraints], the parent would pass in the appropriate [Constraints]
1251/// subclass, and instead of reading the child's size, the parent would read
1252/// whatever the output of [layout] is for that layout protocol. The
1253/// `parentUsesSize` flag is still used to indicate whether the parent is going
1254/// to read that output, and optimizations still kick in if the child has tight
1255/// constraints (as defined by [Constraints.isTight]).
1256///
1257/// ### Painting
1258///
1259/// To describe how a render box paints, implement the [paint] method. It is
1260/// given a [PaintingContext] object and an [Offset]. The painting context
1261/// provides methods to affect the layer tree as well as a
1262/// [PaintingContext.canvas] which can be used to add drawing commands. The
1263/// canvas object should not be cached across calls to the [PaintingContext]'s
1264/// methods; every time a method on [PaintingContext] is called, there is a
1265/// chance that the canvas will change identity. The offset specifies the
1266/// position of the top left corner of the box in the coordinate system of the
1267/// [PaintingContext.canvas].
1268///
1269/// To draw text on a canvas, use a [TextPainter].
1270///
1271/// To draw an image to a canvas, use the [paintImage] method.
1272///
1273/// A [RenderBox] that uses methods on [PaintingContext] that introduce new
1274/// layers should override the [alwaysNeedsCompositing] getter and set it to
1275/// true. If the object sometimes does and sometimes does not, it can have that
1276/// getter return true in some cases and false in others. In that case, whenever
1277/// the return value would change, call [markNeedsCompositingBitsUpdate]. (This
1278/// is done automatically when a child is added or removed, so you don't have to
1279/// call it explicitly if the [alwaysNeedsCompositing] getter only changes value
1280/// based on the presence or absence of children.)
1281///
1282/// Anytime anything changes on the object that would cause the [paint] method
1283/// to paint something different (but would not cause the layout to change),
1284/// the object should call [markNeedsPaint].
1285///
1286/// #### Painting children
1287///
1288/// The [paint] method's `context` argument has a [PaintingContext.paintChild]
1289/// method, which should be called for each child that is to be painted. It
1290/// should be given a reference to the child, and an [Offset] giving the
1291/// position of the child relative to the parent.
1292///
1293/// If the [paint] method applies a transform to the painting context before
1294/// painting children (or generally applies an additional offset beyond the
1295/// offset it was itself given as an argument), then the [applyPaintTransform]
1296/// method should also be overridden. That method must adjust the matrix that it
1297/// is given in the same manner as it transformed the painting context and
1298/// offset before painting the given child. This is used by the [globalToLocal]
1299/// and [localToGlobal] methods.
1300///
1301/// #### Hit Tests
1302///
1303/// Hit testing for render boxes is implemented by the [hitTest] method. The
1304/// default implementation of this method defers to [hitTestSelf] and
1305/// [hitTestChildren]. When implementing hit testing, you can either override
1306/// these latter two methods, or ignore them and just override [hitTest].
1307///
1308/// The [hitTest] method itself is given an [Offset], and must return true if the
1309/// object or one of its children has absorbed the hit (preventing objects below
1310/// this one from being hit), or false if the hit can continue to other objects
1311/// below this one.
1312///
1313/// For each child [RenderBox], the [hitTest] method on the child should be
1314/// called with the same [HitTestResult] argument and with the point transformed
1315/// into the child's coordinate space (in the same manner that the
1316/// [applyPaintTransform] method would). The default implementation defers to
1317/// [hitTestChildren] to call the children. [RenderBoxContainerDefaultsMixin]
1318/// provides a [RenderBoxContainerDefaultsMixin.defaultHitTestChildren] method
1319/// that does this assuming that the children are axis-aligned, not transformed,
1320/// and positioned according to the [BoxParentData.offset] field of the
1321/// [parentData]; more elaborate boxes can override [hitTestChildren]
1322/// accordingly.
1323///
1324/// If the object is hit, then it should also add itself to the [HitTestResult]
1325/// object that is given as an argument to the [hitTest] method, using
1326/// [HitTestResult.add]. The default implementation defers to [hitTestSelf] to
1327/// determine if the box is hit. If the object adds itself before the children
1328/// can add themselves, then it will be as if the object was above the children.
1329/// If it adds itself after the children, then it will be as if it was below the
1330/// children. Entries added to the [HitTestResult] object should use the
1331/// [BoxHitTestEntry] class. The entries are subsequently walked by the system
1332/// in the order they were added, and for each entry, the target's [handleEvent]
1333/// method is called, passing in the [HitTestEntry] object.
1334///
1335/// Hit testing cannot rely on painting having happened.
1336///
1337/// ### Semantics
1338///
1339/// For a render box to be accessible, implement the
1340/// [describeApproximatePaintClip], [visitChildrenForSemantics], and
1341/// [describeSemanticsConfiguration] methods. The default implementations are
1342/// sufficient for objects that only affect layout, but nodes that represent
1343/// interactive components or information (diagrams, text, images, etc) should
1344/// provide more complete implementations. For more information, see the
1345/// documentation for these members.
1346///
1347/// ### Intrinsics and Baselines
1348///
1349/// The layout, painting, hit testing, and semantics protocols are common to all
1350/// render objects. [RenderBox] objects must implement two additional protocols:
1351/// intrinsic sizing and baseline measurements.
1352///
1353/// There are four methods to implement for intrinsic sizing, to compute the
1354/// minimum and maximum intrinsic width and height of the box. The documentation
1355/// for these methods discusses the protocol in detail:
1356/// [computeMinIntrinsicWidth], [computeMaxIntrinsicWidth],
1357/// [computeMinIntrinsicHeight], [computeMaxIntrinsicHeight].
1358///
1359/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if you
1360/// do override any of these methods, which will add additional checks to
1361/// help validate your implementation.
1362///
1363/// In addition, if the box has any children, it must implement
1364/// [computeDistanceToActualBaseline]. [RenderProxyBox] provides a simple
1365/// implementation that forwards to the child; [RenderShiftedBox] provides an
1366/// implementation that offsets the child's baseline information by the position
1367/// of the child relative to the parent. If you do not inherited from either of
1368/// these classes, however, you must implement the algorithm yourself.
1369abstract class RenderBox extends RenderObject {
1370 @override
1371 void setupParentData(covariant RenderObject child) {
1372 if (child.parentData is! BoxParentData) {
1373 child.parentData = BoxParentData();
1374 }
1375 }
1376
1377 Map<_IntrinsicDimensionsCacheEntry, double>? _cachedIntrinsicDimensions;
1378 static int _debugIntrinsicsDepth = 0;
1379
1380 double _computeIntrinsicDimension(_IntrinsicDimension dimension, double argument, double Function(double argument) computer) {
1381 assert(RenderObject.debugCheckingIntrinsics || !debugDoingThisResize); // performResize should not depend on anything except the incoming constraints
1382 bool shouldCache = true;
1383 assert(() {
1384 // we don't want the checked-mode intrinsic tests to affect
1385 // who gets marked dirty, etc.
1386 if (RenderObject.debugCheckingIntrinsics) {
1387 shouldCache = false;
1388 }
1389 return true;
1390 }());
1391 if (shouldCache) {
1392 Map<String, String>? debugTimelineArguments;
1393 assert(() {
1394 if (debugEnhanceLayoutTimelineArguments) {
1395 debugTimelineArguments = toDiagnosticsNode().toTimelineArguments();
1396 } else {
1397 debugTimelineArguments = <String, String>{};
1398 }
1399 debugTimelineArguments!['intrinsics dimension'] = dimension.name;
1400 debugTimelineArguments!['intrinsics argument'] = '$argument';
1401 return true;
1402 }());
1403 if (!kReleaseMode) {
1404 if (debugProfileLayoutsEnabled || _debugIntrinsicsDepth == 0) {
1405 FlutterTimeline.startSync(
1406 '$runtimeType intrinsics',
1407 arguments: debugTimelineArguments,
1408 );
1409 }
1410 _debugIntrinsicsDepth += 1;
1411 }
1412 _cachedIntrinsicDimensions ??= <_IntrinsicDimensionsCacheEntry, double>{};
1413 final double result = _cachedIntrinsicDimensions!.putIfAbsent(
1414 _IntrinsicDimensionsCacheEntry(dimension, argument),
1415 () => computer(argument),
1416 );
1417 if (!kReleaseMode) {
1418 _debugIntrinsicsDepth -= 1;
1419 if (debugProfileLayoutsEnabled || _debugIntrinsicsDepth == 0) {
1420 FlutterTimeline.finishSync();
1421 }
1422 }
1423 return result;
1424 }
1425 return computer(argument);
1426 }
1427
1428 /// Returns the minimum width that this box could be without failing to
1429 /// correctly paint its contents within itself, without clipping.
1430 ///
1431 /// The height argument may give a specific height to assume. The given height
1432 /// can be infinite, meaning that the intrinsic width in an unconstrained
1433 /// environment is being requested. The given height should never be negative
1434 /// or null.
1435 ///
1436 /// This function should only be called on one's children. Calling this
1437 /// function couples the child with the parent so that when the child's layout
1438 /// changes, the parent is notified (via [markNeedsLayout]).
1439 ///
1440 /// Calling this function is expensive as it can result in O(N^2) behavior.
1441 ///
1442 /// Do not override this method. Instead, implement [computeMinIntrinsicWidth].
1443 @mustCallSuper
1444 double getMinIntrinsicWidth(double height) {
1445 assert(() {
1446 if (height < 0.0) {
1447 throw FlutterError.fromParts(<DiagnosticsNode>[
1448 ErrorSummary('The height argument to getMinIntrinsicWidth was negative.'),
1449 ErrorDescription('The argument to getMinIntrinsicWidth must not be negative or null.'),
1450 ErrorHint(
1451 'If you perform computations on another height before passing it to '
1452 'getMinIntrinsicWidth, consider using math.max() or double.clamp() '
1453 'to force the value into the valid range.',
1454 ),
1455 ]);
1456 }
1457 return true;
1458 }());
1459 return _computeIntrinsicDimension(_IntrinsicDimension.minWidth, height, computeMinIntrinsicWidth);
1460 }
1461
1462 /// Computes the value returned by [getMinIntrinsicWidth]. Do not call this
1463 /// function directly, instead, call [getMinIntrinsicWidth].
1464 ///
1465 /// Override in subclasses that implement [performLayout]. This method should
1466 /// return the minimum width that this box could be without failing to
1467 /// correctly paint its contents within itself, without clipping.
1468 ///
1469 /// If the layout algorithm is independent of the context (e.g. it always
1470 /// tries to be a particular size), or if the layout algorithm is
1471 /// width-in-height-out, or if the layout algorithm uses both the incoming
1472 /// width and height constraints (e.g. it always sizes itself to
1473 /// [BoxConstraints.biggest]), then the `height` argument should be ignored.
1474 ///
1475 /// If the layout algorithm is strictly height-in-width-out, or is
1476 /// height-in-width-out when the width is unconstrained, then the height
1477 /// argument is the height to use.
1478 ///
1479 /// The `height` argument will never be negative or null. It may be infinite.
1480 ///
1481 /// If this algorithm depends on the intrinsic dimensions of a child, the
1482 /// intrinsic dimensions of that child should be obtained using the functions
1483 /// whose names start with `get`, not `compute`.
1484 ///
1485 /// This function should never return a negative or infinite value.
1486 ///
1487 /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
1488 /// you do override this method, which will add additional checks to help
1489 /// validate your implementation.
1490 ///
1491 /// ## Examples
1492 ///
1493 /// ### Text
1494 ///
1495 /// English text is the canonical example of a width-in-height-out algorithm.
1496 /// The `height` argument is therefore ignored.
1497 ///
1498 /// Consider the string "Hello World". The _maximum_ intrinsic width (as
1499 /// returned from [computeMaxIntrinsicWidth]) would be the width of the string
1500 /// with no line breaks.
1501 ///
1502 /// The minimum intrinsic width would be the width of the widest word, "Hello"
1503 /// or "World". If the text is rendered in an even narrower width, however, it
1504 /// might still not overflow. For example, maybe the rendering would put a
1505 /// line-break half-way through the words, as in "Hel⁞lo⁞Wor⁞ld". However,
1506 /// this wouldn't be a _correct_ rendering, and [computeMinIntrinsicWidth] is
1507 /// defined as returning the minimum width that the box could be without
1508 /// failing to _correctly_ paint the contents within itself.
1509 ///
1510 /// The minimum intrinsic _height_ for a given width _smaller_ than the
1511 /// minimum intrinsic width could therefore be greater than the minimum
1512 /// intrinsic height for the minimum intrinsic width.
1513 ///
1514 /// ### Viewports (e.g. scrolling lists)
1515 ///
1516 /// Some render boxes are intended to clip their children. For example, the
1517 /// render box for a scrolling list might always size itself to its parents'
1518 /// size (or rather, to the maximum incoming constraints), regardless of the
1519 /// children's sizes, and then clip the children and position them based on
1520 /// the current scroll offset.
1521 ///
1522 /// The intrinsic dimensions in these cases still depend on the children, even
1523 /// though the layout algorithm sizes the box in a way independent of the
1524 /// children. It is the size that is needed to paint the box's contents (in
1525 /// this case, the children) _without clipping_ that matters.
1526 ///
1527 /// ### When the intrinsic dimensions cannot be known
1528 ///
1529 /// There are cases where render objects do not have an efficient way to
1530 /// compute their intrinsic dimensions. For example, it may be prohibitively
1531 /// expensive to reify and measure every child of a lazy viewport (viewports
1532 /// generally only instantiate the actually visible children), or the
1533 /// dimensions may be computed by a callback about which the render object
1534 /// cannot reason.
1535 ///
1536 /// In such cases, it may be impossible (or at least impractical) to actually
1537 /// return a valid answer. In such cases, the intrinsic functions should throw
1538 /// when [RenderObject.debugCheckingIntrinsics] is false and asserts are
1539 /// enabled, and return 0.0 otherwise.
1540 ///
1541 /// See the implementations of [LayoutBuilder] or [RenderViewportBase] for
1542 /// examples (in particular,
1543 /// [RenderViewportBase.debugThrowIfNotCheckingIntrinsics]).
1544 ///
1545 /// ### Aspect-ratio-driven boxes
1546 ///
1547 /// Some boxes always return a fixed size based on the constraints. For these
1548 /// boxes, the intrinsic functions should return the appropriate size when the
1549 /// incoming `height` or `width` argument is finite, treating that as a tight
1550 /// constraint in the respective direction and treating the other direction's
1551 /// constraints as unbounded. This is because the definitions of
1552 /// [computeMinIntrinsicWidth] and [computeMinIntrinsicHeight] are in terms of
1553 /// what the dimensions _could be_, and such boxes can only be one size in
1554 /// such cases.
1555 ///
1556 /// When the incoming argument is not finite, then they should return the
1557 /// actual intrinsic dimensions based on the contents, as any other box would.
1558 ///
1559 /// See also:
1560 ///
1561 /// * [computeMaxIntrinsicWidth], which computes the smallest width beyond
1562 /// which increasing the width never decreases the preferred height.
1563 @protected
1564 double computeMinIntrinsicWidth(double height) {
1565 return 0.0;
1566 }
1567
1568 /// Returns the smallest width beyond which increasing the width never
1569 /// decreases the preferred height. The preferred height is the value that
1570 /// would be returned by [getMinIntrinsicHeight] for that width.
1571 ///
1572 /// The height argument may give a specific height to assume. The given height
1573 /// can be infinite, meaning that the intrinsic width in an unconstrained
1574 /// environment is being requested. The given height should never be negative
1575 /// or null.
1576 ///
1577 /// This function should only be called on one's children. Calling this
1578 /// function couples the child with the parent so that when the child's layout
1579 /// changes, the parent is notified (via [markNeedsLayout]).
1580 ///
1581 /// Calling this function is expensive as it can result in O(N^2) behavior.
1582 ///
1583 /// Do not override this method. Instead, implement
1584 /// [computeMaxIntrinsicWidth].
1585 @mustCallSuper
1586 double getMaxIntrinsicWidth(double height) {
1587 assert(() {
1588 if (height < 0.0) {
1589 throw FlutterError.fromParts(<DiagnosticsNode>[
1590 ErrorSummary('The height argument to getMaxIntrinsicWidth was negative.'),
1591 ErrorDescription('The argument to getMaxIntrinsicWidth must not be negative or null.'),
1592 ErrorHint(
1593 'If you perform computations on another height before passing it to '
1594 'getMaxIntrinsicWidth, consider using math.max() or double.clamp() '
1595 'to force the value into the valid range.',
1596 ),
1597 ]);
1598 }
1599 return true;
1600 }());
1601 return _computeIntrinsicDimension(_IntrinsicDimension.maxWidth, height, computeMaxIntrinsicWidth);
1602 }
1603
1604 /// Computes the value returned by [getMaxIntrinsicWidth]. Do not call this
1605 /// function directly, instead, call [getMaxIntrinsicWidth].
1606 ///
1607 /// Override in subclasses that implement [performLayout]. This should return
1608 /// the smallest width beyond which increasing the width never decreases the
1609 /// preferred height. The preferred height is the value that would be returned
1610 /// by [computeMinIntrinsicHeight] for that width.
1611 ///
1612 /// If the layout algorithm is strictly height-in-width-out, or is
1613 /// height-in-width-out when the width is unconstrained, then this should
1614 /// return the same value as [computeMinIntrinsicWidth] for the same height.
1615 ///
1616 /// Otherwise, the height argument should be ignored, and the returned value
1617 /// should be equal to or bigger than the value returned by
1618 /// [computeMinIntrinsicWidth].
1619 ///
1620 /// The `height` argument will never be negative or null. It may be infinite.
1621 ///
1622 /// The value returned by this method might not match the size that the object
1623 /// would actually take. For example, a [RenderBox] subclass that always
1624 /// exactly sizes itself using [BoxConstraints.biggest] might well size itself
1625 /// bigger than its max intrinsic size.
1626 ///
1627 /// If this algorithm depends on the intrinsic dimensions of a child, the
1628 /// intrinsic dimensions of that child should be obtained using the functions
1629 /// whose names start with `get`, not `compute`.
1630 ///
1631 /// This function should never return a negative or infinite value.
1632 ///
1633 /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
1634 /// you do override this method, which will add additional checks to help
1635 /// validate your implementation.
1636 ///
1637 /// See also:
1638 ///
1639 /// * [computeMinIntrinsicWidth], which has usage examples.
1640 @protected
1641 double computeMaxIntrinsicWidth(double height) {
1642 return 0.0;
1643 }
1644
1645 /// Returns the minimum height that this box could be without failing to
1646 /// correctly paint its contents within itself, without clipping.
1647 ///
1648 /// The width argument may give a specific width to assume. The given width
1649 /// can be infinite, meaning that the intrinsic height in an unconstrained
1650 /// environment is being requested. The given width should never be negative
1651 /// or null.
1652 ///
1653 /// This function should only be called on one's children. Calling this
1654 /// function couples the child with the parent so that when the child's layout
1655 /// changes, the parent is notified (via [markNeedsLayout]).
1656 ///
1657 /// Calling this function is expensive as it can result in O(N^2) behavior.
1658 ///
1659 /// Do not override this method. Instead, implement
1660 /// [computeMinIntrinsicHeight].
1661 @mustCallSuper
1662 double getMinIntrinsicHeight(double width) {
1663 assert(() {
1664 if (width < 0.0) {
1665 throw FlutterError.fromParts(<DiagnosticsNode>[
1666 ErrorSummary('The width argument to getMinIntrinsicHeight was negative.'),
1667 ErrorDescription('The argument to getMinIntrinsicHeight must not be negative or null.'),
1668 ErrorHint(
1669 'If you perform computations on another width before passing it to '
1670 'getMinIntrinsicHeight, consider using math.max() or double.clamp() '
1671 'to force the value into the valid range.',
1672 ),
1673 ]);
1674 }
1675 return true;
1676 }());
1677 return _computeIntrinsicDimension(_IntrinsicDimension.minHeight, width, computeMinIntrinsicHeight);
1678 }
1679
1680 /// Computes the value returned by [getMinIntrinsicHeight]. Do not call this
1681 /// function directly, instead, call [getMinIntrinsicHeight].
1682 ///
1683 /// Override in subclasses that implement [performLayout]. Should return the
1684 /// minimum height that this box could be without failing to correctly paint
1685 /// its contents within itself, without clipping.
1686 ///
1687 /// If the layout algorithm is independent of the context (e.g. it always
1688 /// tries to be a particular size), or if the layout algorithm is
1689 /// height-in-width-out, or if the layout algorithm uses both the incoming
1690 /// height and width constraints (e.g. it always sizes itself to
1691 /// [BoxConstraints.biggest]), then the `width` argument should be ignored.
1692 ///
1693 /// If the layout algorithm is strictly width-in-height-out, or is
1694 /// width-in-height-out when the height is unconstrained, then the width
1695 /// argument is the width to use.
1696 ///
1697 /// The `width` argument will never be negative or null. It may be infinite.
1698 ///
1699 /// If this algorithm depends on the intrinsic dimensions of a child, the
1700 /// intrinsic dimensions of that child should be obtained using the functions
1701 /// whose names start with `get`, not `compute`.
1702 ///
1703 /// This function should never return a negative or infinite value.
1704 ///
1705 /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
1706 /// you do override this method, which will add additional checks to help
1707 /// validate your implementation.
1708 ///
1709 /// See also:
1710 ///
1711 /// * [computeMinIntrinsicWidth], which has usage examples.
1712 /// * [computeMaxIntrinsicHeight], which computes the smallest height beyond
1713 /// which increasing the height never decreases the preferred width.
1714 @protected
1715 double computeMinIntrinsicHeight(double width) {
1716 return 0.0;
1717 }
1718
1719 /// Returns the smallest height beyond which increasing the height never
1720 /// decreases the preferred width. The preferred width is the value that
1721 /// would be returned by [getMinIntrinsicWidth] for that height.
1722 ///
1723 /// The width argument may give a specific width to assume. The given width
1724 /// can be infinite, meaning that the intrinsic height in an unconstrained
1725 /// environment is being requested. The given width should never be negative
1726 /// or null.
1727 ///
1728 /// This function should only be called on one's children. Calling this
1729 /// function couples the child with the parent so that when the child's layout
1730 /// changes, the parent is notified (via [markNeedsLayout]).
1731 ///
1732 /// Calling this function is expensive as it can result in O(N^2) behavior.
1733 ///
1734 /// Do not override this method. Instead, implement
1735 /// [computeMaxIntrinsicHeight].
1736 @mustCallSuper
1737 double getMaxIntrinsicHeight(double width) {
1738 assert(() {
1739 if (width < 0.0) {
1740 throw FlutterError.fromParts(<DiagnosticsNode>[
1741 ErrorSummary('The width argument to getMaxIntrinsicHeight was negative.'),
1742 ErrorDescription('The argument to getMaxIntrinsicHeight must not be negative or null.'),
1743 ErrorHint(
1744 'If you perform computations on another width before passing it to '
1745 'getMaxIntrinsicHeight, consider using math.max() or double.clamp() '
1746 'to force the value into the valid range.',
1747 ),
1748 ]);
1749 }
1750 return true;
1751 }());
1752 return _computeIntrinsicDimension(_IntrinsicDimension.maxHeight, width, computeMaxIntrinsicHeight);
1753 }
1754
1755 /// Computes the value returned by [getMaxIntrinsicHeight]. Do not call this
1756 /// function directly, instead, call [getMaxIntrinsicHeight].
1757 ///
1758 /// Override in subclasses that implement [performLayout]. Should return the
1759 /// smallest height beyond which increasing the height never decreases the
1760 /// preferred width. The preferred width is the value that would be returned
1761 /// by [computeMinIntrinsicWidth] for that height.
1762 ///
1763 /// If the layout algorithm is strictly width-in-height-out, or is
1764 /// width-in-height-out when the height is unconstrained, then this should
1765 /// return the same value as [computeMinIntrinsicHeight] for the same width.
1766 ///
1767 /// Otherwise, the width argument should be ignored, and the returned value
1768 /// should be equal to or bigger than the value returned by
1769 /// [computeMinIntrinsicHeight].
1770 ///
1771 /// The `width` argument will never be negative or null. It may be infinite.
1772 ///
1773 /// The value returned by this method might not match the size that the object
1774 /// would actually take. For example, a [RenderBox] subclass that always
1775 /// exactly sizes itself using [BoxConstraints.biggest] might well size itself
1776 /// bigger than its max intrinsic size.
1777 ///
1778 /// If this algorithm depends on the intrinsic dimensions of a child, the
1779 /// intrinsic dimensions of that child should be obtained using the functions
1780 /// whose names start with `get`, not `compute`.
1781 ///
1782 /// This function should never return a negative or infinite value.
1783 ///
1784 /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
1785 /// you do override this method, which will add additional checks to help
1786 /// validate your implementation.
1787 ///
1788 /// See also:
1789 ///
1790 /// * [computeMinIntrinsicWidth], which has usage examples.
1791 @protected
1792 double computeMaxIntrinsicHeight(double width) {
1793 return 0.0;
1794 }
1795
1796 Map<BoxConstraints, Size>? _cachedDryLayoutSizes;
1797 bool _computingThisDryLayout = false;
1798
1799 /// Returns the [Size] that this [RenderBox] would like to be given the
1800 /// provided [BoxConstraints].
1801 ///
1802 /// The size returned by this method is guaranteed to be the same size that
1803 /// this [RenderBox] computes for itself during layout given the same
1804 /// constraints.
1805 ///
1806 /// This function should only be called on one's children. Calling this
1807 /// function couples the child with the parent so that when the child's layout
1808 /// changes, the parent is notified (via [markNeedsLayout]).
1809 ///
1810 /// This layout is called "dry" layout as opposed to the regular "wet" layout
1811 /// run performed by [performLayout] because it computes the desired size for
1812 /// the given constraints without changing any internal state.
1813 ///
1814 /// Calling this function is expensive as it can result in O(N^2) behavior.
1815 ///
1816 /// Do not override this method. Instead, implement [computeDryLayout].
1817 @mustCallSuper
1818 Size getDryLayout(BoxConstraints constraints) {
1819 bool shouldCache = true;
1820 assert(() {
1821 // we don't want the checked-mode intrinsic tests to affect
1822 // who gets marked dirty, etc.
1823 if (RenderObject.debugCheckingIntrinsics) {
1824 shouldCache = false;
1825 }
1826 return true;
1827 }());
1828 if (shouldCache) {
1829 Map<String, String>? debugTimelineArguments;
1830 assert(() {
1831 if (debugEnhanceLayoutTimelineArguments) {
1832 debugTimelineArguments = toDiagnosticsNode().toTimelineArguments();
1833 } else {
1834 debugTimelineArguments = <String, String>{};
1835 }
1836 debugTimelineArguments!['getDryLayout constraints'] = '$constraints';
1837 return true;
1838 }());
1839 if (!kReleaseMode) {
1840 if (debugProfileLayoutsEnabled || _debugIntrinsicsDepth == 0) {
1841 FlutterTimeline.startSync(
1842 '$runtimeType.getDryLayout',
1843 arguments: debugTimelineArguments,
1844 );
1845 }
1846 _debugIntrinsicsDepth += 1;
1847 }
1848 _cachedDryLayoutSizes ??= <BoxConstraints, Size>{};
1849 final Size result = _cachedDryLayoutSizes!.putIfAbsent(constraints, () => _computeDryLayout(constraints));
1850 if (!kReleaseMode) {
1851 _debugIntrinsicsDepth -= 1;
1852 if (debugProfileLayoutsEnabled || _debugIntrinsicsDepth == 0) {
1853 FlutterTimeline.finishSync();
1854 }
1855 }
1856 return result;
1857 }
1858 return _computeDryLayout(constraints);
1859 }
1860
1861 Size _computeDryLayout(BoxConstraints constraints) {
1862 assert(() {
1863 assert(!_computingThisDryLayout);
1864 _computingThisDryLayout = true;
1865 return true;
1866 }());
1867 final Size result = computeDryLayout(constraints);
1868 assert(() {
1869 assert(_computingThisDryLayout);
1870 _computingThisDryLayout = false;
1871 return true;
1872 }());
1873 return result;
1874 }
1875
1876 /// Computes the value returned by [getDryLayout]. Do not call this
1877 /// function directly, instead, call [getDryLayout].
1878 ///
1879 /// Override in subclasses that implement [performLayout] or [performResize]
1880 /// or when setting [sizedByParent] to true without overriding
1881 /// [performResize]. This method should return the [Size] that this
1882 /// [RenderBox] would like to be given the provided [BoxConstraints].
1883 ///
1884 /// The size returned by this method must match the [size] that the
1885 /// [RenderBox] will compute for itself in [performLayout] (or
1886 /// [performResize], if [sizedByParent] is true).
1887 ///
1888 /// If this algorithm depends on the size of a child, the size of that child
1889 /// should be obtained using its [getDryLayout] method.
1890 ///
1891 /// This layout is called "dry" layout as opposed to the regular "wet" layout
1892 /// run performed by [performLayout] because it computes the desired size for
1893 /// the given constraints without changing any internal state.
1894 ///
1895 /// ### When the size cannot be known
1896 ///
1897 /// There are cases where render objects do not have an efficient way to
1898 /// compute their size without doing a full layout. For example, the size
1899 /// may depend on the baseline of a child (which is not available without
1900 /// doing a full layout), it may be computed by a callback about which the
1901 /// render object cannot reason, or the layout is so complex that it
1902 /// is impractical to calculate the size in an efficient way.
1903 ///
1904 /// In such cases, it may be impossible (or at least impractical) to actually
1905 /// return a valid answer. In such cases, the function should call
1906 /// [debugCannotComputeDryLayout] from within an assert and return a dummy
1907 /// value of `const Size(0, 0)`.
1908 @protected
1909 Size computeDryLayout(covariant BoxConstraints constraints) {
1910 assert(debugCannotComputeDryLayout(
1911 error: FlutterError.fromParts(<DiagnosticsNode>[
1912 ErrorSummary('The ${objectRuntimeType(this, 'RenderBox')} class does not implement "computeDryLayout".'),
1913 ErrorHint(
1914 'If you are not writing your own RenderBox subclass, then this is not\n'
1915 'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.yml',
1916 ),
1917 ]),
1918 ));
1919 return Size.zero;
1920 }
1921
1922 static bool _dryLayoutCalculationValid = true;
1923
1924 /// Called from [computeDryLayout] within an assert if the given [RenderBox]
1925 /// subclass does not support calculating a dry layout.
1926 ///
1927 /// When asserts are enabled and [debugCheckingIntrinsics] is not true, this
1928 /// method will either throw the provided [FlutterError] or it will create and
1929 /// throw a [FlutterError] with the provided `reason`. Otherwise, it will
1930 /// return true.
1931 ///
1932 /// One of the arguments has to be provided.
1933 ///
1934 /// See also:
1935 ///
1936 /// * [computeDryLayout], which lists some reasons why it may not be feasible
1937 /// to compute the dry layout.
1938 bool debugCannotComputeDryLayout({String? reason, FlutterError? error}) {
1939 assert((reason == null) != (error == null));
1940 assert(() {
1941 if (!RenderObject.debugCheckingIntrinsics) {
1942 if (reason != null) {
1943 assert(error ==null);
1944 throw FlutterError.fromParts(<DiagnosticsNode>[
1945 ErrorSummary('The ${objectRuntimeType(this, 'RenderBox')} class does not support dry layout.'),
1946 if (reason.isNotEmpty) ErrorDescription(reason),
1947 ]);
1948 }
1949 assert(error != null);
1950 throw error!;
1951 }
1952 _dryLayoutCalculationValid = false;
1953 return true;
1954 }());
1955 return true;
1956 }
1957
1958 /// Whether this render object has undergone layout and has a [size].
1959 bool get hasSize => _size != null;
1960
1961 /// The size of this render box computed during layout.
1962 ///
1963 /// This value is stale whenever this object is marked as needing layout.
1964 /// During [performLayout], do not read the size of a child unless you pass
1965 /// true for parentUsesSize when calling the child's [layout] function.
1966 ///
1967 /// The size of a box should be set only during the box's [performLayout] or
1968 /// [performResize] functions. If you wish to change the size of a box outside
1969 /// of those functions, call [markNeedsLayout] instead to schedule a layout of
1970 /// the box.
1971 Size get size {
1972 assert(hasSize, 'RenderBox was not laid out: $this');
1973 assert(() {
1974 final Size? size = _size;
1975 if (size is _DebugSize) {
1976 assert(size._owner == this);
1977 if (RenderObject.debugActiveLayout != null &&
1978 !RenderObject.debugActiveLayout!.debugDoingThisLayoutWithCallback) {
1979 assert(
1980 debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||
1981 (RenderObject.debugActiveLayout == parent && size._canBeUsedByParent),
1982 'RenderBox.size accessed beyond the scope of resize, layout, or '
1983 'permitted parent access. RenderBox can always access its own size, '
1984 'otherwise, the only object that is allowed to read RenderBox.size '
1985 'is its parent, if they have said they will. It you hit this assert '
1986 'trying to access a child\'s size, pass "parentUsesSize: true" to '
1987 "that child's layout().",
1988 );
1989 }
1990 assert(size == _size);
1991 }
1992 return true;
1993 }());
1994 return _size ?? (throw StateError('RenderBox was not laid out: $runtimeType#${shortHash(this)}'));
1995 }
1996 Size? _size;
1997 /// Setting the size, in debug mode, triggers some analysis of the render box,
1998 /// as implemented by [debugAssertDoesMeetConstraints], including calling the intrinsic
1999 /// sizing methods and checking that they meet certain invariants.
2000 @protected
2001 set size(Size value) {
2002 assert(!(debugDoingThisResize && debugDoingThisLayout));
2003 assert(sizedByParent || !debugDoingThisResize);
2004 assert(() {
2005 if ((sizedByParent && debugDoingThisResize) ||
2006 (!sizedByParent && debugDoingThisLayout)) {
2007 return true;
2008 }
2009 assert(!debugDoingThisResize);
2010 final List<DiagnosticsNode> information = <DiagnosticsNode>[
2011 ErrorSummary('RenderBox size setter called incorrectly.'),
2012 ];
2013 if (debugDoingThisLayout) {
2014 assert(sizedByParent);
2015 information.add(ErrorDescription('It appears that the size setter was called from performLayout().'));
2016 } else {
2017 information.add(ErrorDescription(
2018 'The size setter was called from outside layout (neither performResize() nor performLayout() were being run for this object).',
2019 ));
2020 if (owner != null && owner!.debugDoingLayout) {
2021 information.add(ErrorDescription('Only the object itself can set its size. It is a contract violation for other objects to set it.'));
2022 }
2023 }
2024 if (sizedByParent) {
2025 information.add(ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().'));
2026 } else {
2027 information.add(ErrorDescription('Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().'));
2028 }
2029 throw FlutterError.fromParts(information);
2030 }());
2031 assert(() {
2032 value = debugAdoptSize(value);
2033 return true;
2034 }());
2035 _size = value;
2036 assert(() {
2037 debugAssertDoesMeetConstraints();
2038 return true;
2039 }());
2040 }
2041
2042 /// Claims ownership of the given [Size].
2043 ///
2044 /// In debug mode, the [RenderBox] class verifies that [Size] objects obtained
2045 /// from other [RenderBox] objects are only used according to the semantics of
2046 /// the [RenderBox] protocol, namely that a [Size] from a [RenderBox] can only
2047 /// be used by its parent, and then only if `parentUsesSize` was set.
2048 ///
2049 /// Sometimes, a [Size] that can validly be used ends up no longer being valid
2050 /// over time. The common example is a [Size] taken from a child that is later
2051 /// removed from the parent. In such cases, this method can be called to first
2052 /// check whether the size can legitimately be used, and if so, to then create
2053 /// a new [Size] that can be used going forward, regardless of what happens to
2054 /// the original owner.
2055 Size debugAdoptSize(Size value) {
2056 Size result = value;
2057 assert(() {
2058 if (value is _DebugSize) {
2059 if (value._owner != this) {
2060 if (value._owner.parent != this) {
2061 throw FlutterError.fromParts(<DiagnosticsNode>[
2062 ErrorSummary('The size property was assigned a size inappropriately.'),
2063 describeForError('The following render object'),
2064 value._owner.describeForError('...was assigned a size obtained from'),
2065 ErrorDescription(
2066 'However, this second render object is not, or is no longer, a '
2067 'child of the first, and it is therefore a violation of the '
2068 'RenderBox layout protocol to use that size in the layout of the '
2069 'first render object.',
2070 ),
2071 ErrorHint(
2072 'If the size was obtained at a time where it was valid to read '
2073 'the size (because the second render object above was a child '
2074 'of the first at the time), then it should be adopted using '
2075 'debugAdoptSize at that time.',
2076 ),
2077 ErrorHint(
2078 'If the size comes from a grandchild or a render object from an '
2079 'entirely different part of the render tree, then there is no '
2080 'way to be notified when the size changes and therefore attempts '
2081 'to read that size are almost certainly a source of bugs. A different '
2082 'approach should be used.',
2083 ),
2084 ]);
2085 }
2086 if (!value._canBeUsedByParent) {
2087 throw FlutterError.fromParts(<DiagnosticsNode>[
2088 ErrorSummary("A child's size was used without setting parentUsesSize."),
2089 describeForError('The following render object'),
2090 value._owner.describeForError('...was assigned a size obtained from its child'),
2091 ErrorDescription(
2092 'However, when the child was laid out, the parentUsesSize argument '
2093 'was not set or set to false. Subsequently this transpired to be '
2094 'inaccurate: the size was nonetheless used by the parent.\n'
2095 'It is important to tell the framework if the size will be used or not '
2096 'as several important performance optimizations can be made if the '
2097 'size will not be used by the parent.',
2098 ),
2099 ]);
2100 }
2101 }
2102 }
2103 result = _DebugSize(value, this, debugCanParentUseSize);
2104 return true;
2105 }());
2106 return result;
2107 }
2108
2109 @override
2110 Rect get semanticBounds => Offset.zero & size;
2111
2112 @override
2113 void debugResetSize() {
2114 // updates the value of size._canBeUsedByParent if necessary
2115 size = size; // ignore: no_self_assignments
2116 }
2117
2118 Map<TextBaseline, double?>? _cachedBaselines;
2119 static bool _debugDoingBaseline = false;
2120 static bool _debugSetDoingBaseline(bool value) {
2121 _debugDoingBaseline = value;
2122 return true;
2123 }
2124
2125 /// Returns the distance from the y-coordinate of the position of the box to
2126 /// the y-coordinate of the first given baseline in the box's contents.
2127 ///
2128 /// Used by certain layout models to align adjacent boxes on a common
2129 /// baseline, regardless of padding, font size differences, etc. If there is
2130 /// no baseline, this function returns the distance from the y-coordinate of
2131 /// the position of the box to the y-coordinate of the bottom of the box
2132 /// (i.e., the height of the box) unless the caller passes true
2133 /// for `onlyReal`, in which case the function returns null.
2134 ///
2135 /// Only call this function after calling [layout] on this box. You
2136 /// are only allowed to call this from the parent of this box during
2137 /// that parent's [performLayout] or [paint] functions.
2138 ///
2139 /// When implementing a [RenderBox] subclass, to override the baseline
2140 /// computation, override [computeDistanceToActualBaseline].
2141 double? getDistanceToBaseline(TextBaseline baseline, { bool onlyReal = false }) {
2142 assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2143 assert(!debugNeedsLayout);
2144 assert(() {
2145 if (owner!.debugDoingLayout) {
2146 return (RenderObject.debugActiveLayout == parent) && parent!.debugDoingThisLayout;
2147 }
2148 if (owner!.debugDoingPaint) {
2149 return ((RenderObject.debugActivePaint == parent) && parent!.debugDoingThisPaint) ||
2150 ((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
2151 }
2152 return false;
2153 }());
2154 assert(_debugSetDoingBaseline(true));
2155 final double? result;
2156 try {
2157 result = getDistanceToActualBaseline(baseline);
2158 } finally {
2159 assert(_debugSetDoingBaseline(false));
2160 }
2161 if (result == null && !onlyReal) {
2162 return size.height;
2163 }
2164 return result;
2165 }
2166
2167 /// Calls [computeDistanceToActualBaseline] and caches the result.
2168 ///
2169 /// This function must only be called from [getDistanceToBaseline] and
2170 /// [computeDistanceToActualBaseline]. Do not call this function directly from
2171 /// outside those two methods.
2172 @protected
2173 @mustCallSuper
2174 double? getDistanceToActualBaseline(TextBaseline baseline) {
2175 assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2176 _cachedBaselines ??= <TextBaseline, double?>{};
2177 return _cachedBaselines!.putIfAbsent(baseline, () => computeDistanceToActualBaseline(baseline));
2178 }
2179
2180 /// Returns the distance from the y-coordinate of the position of the box to
2181 /// the y-coordinate of the first given baseline in the box's contents, if
2182 /// any, or null otherwise.
2183 ///
2184 /// Do not call this function directly. If you need to know the baseline of a
2185 /// child from an invocation of [performLayout] or [paint], call
2186 /// [getDistanceToBaseline].
2187 ///
2188 /// Subclasses should override this method to supply the distances to their
2189 /// baselines. When implementing this method, there are generally three
2190 /// strategies:
2191 ///
2192 /// * For classes that use the [ContainerRenderObjectMixin] child model,
2193 /// consider mixing in the [RenderBoxContainerDefaultsMixin] class and
2194 /// using
2195 /// [RenderBoxContainerDefaultsMixin.defaultComputeDistanceToFirstActualBaseline].
2196 ///
2197 /// * For classes that define a particular baseline themselves, return that
2198 /// value directly.
2199 ///
2200 /// * For classes that have a child to which they wish to defer the
2201 /// computation, call [getDistanceToActualBaseline] on the child (not
2202 /// [computeDistanceToActualBaseline], the internal implementation, and not
2203 /// [getDistanceToBaseline], the public entry point for this API).
2204 @protected
2205 double? computeDistanceToActualBaseline(TextBaseline baseline) {
2206 assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2207 return null;
2208 }
2209
2210 /// The box constraints most recently received from the parent.
2211 @override
2212 BoxConstraints get constraints => super.constraints as BoxConstraints;
2213
2214 @override
2215 void debugAssertDoesMeetConstraints() {
2216 assert(() {
2217 if (!hasSize) {
2218 final DiagnosticsNode contract;
2219 if (sizedByParent) {
2220 contract = ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().');
2221 } else {
2222 contract = ErrorDescription('Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().');
2223 }
2224 throw FlutterError.fromParts(<DiagnosticsNode>[
2225 ErrorSummary('RenderBox did not set its size during layout.'),
2226 contract,
2227 ErrorDescription('It appears that this did not happen; layout completed, but the size property is still null.'),
2228 DiagnosticsProperty<RenderBox>('The RenderBox in question is', this, style: DiagnosticsTreeStyle.errorProperty),
2229 ]);
2230 }
2231 // verify that the size is not infinite
2232 if (!_size!.isFinite) {
2233 final List<DiagnosticsNode> information = <DiagnosticsNode>[
2234 ErrorSummary('$runtimeType object was given an infinite size during layout.'),
2235 ErrorDescription(
2236 'This probably means that it is a render object that tries to be '
2237 'as big as possible, but it was put inside another render object '
2238 'that allows its children to pick their own size.',
2239 ),
2240 ];
2241 if (!constraints.hasBoundedWidth) {
2242 RenderBox node = this;
2243 while (!node.constraints.hasBoundedWidth && node.parent is RenderBox) {
2244 node = node.parent! as RenderBox;
2245 }
2246
2247 information.add(node.describeForError('The nearest ancestor providing an unbounded width constraint is'));
2248 }
2249 if (!constraints.hasBoundedHeight) {
2250 RenderBox node = this;
2251 while (!node.constraints.hasBoundedHeight && node.parent is RenderBox) {
2252 node = node.parent! as RenderBox;
2253 }
2254
2255 information.add(node.describeForError('The nearest ancestor providing an unbounded height constraint is'));
2256 }
2257 throw FlutterError.fromParts(<DiagnosticsNode>[
2258 ...information,
2259 DiagnosticsProperty<BoxConstraints>('The constraints that applied to the $runtimeType were', constraints, style: DiagnosticsTreeStyle.errorProperty),
2260 DiagnosticsProperty<Size>('The exact size it was given was', _size, style: DiagnosticsTreeStyle.errorProperty),
2261 ErrorHint('See https://flutter.dev/docs/development/ui/layout/box-constraints for more information.'),
2262 ]);
2263 }
2264 // verify that the size is within the constraints
2265 if (!constraints.isSatisfiedBy(_size!)) {
2266 throw FlutterError.fromParts(<DiagnosticsNode>[
2267 ErrorSummary('$runtimeType does not meet its constraints.'),
2268 DiagnosticsProperty<BoxConstraints>('Constraints', constraints, style: DiagnosticsTreeStyle.errorProperty),
2269 DiagnosticsProperty<Size>('Size', _size, style: DiagnosticsTreeStyle.errorProperty),
2270 ErrorHint(
2271 'If you are not writing your own RenderBox subclass, then this is not '
2272 'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.yml',
2273 ),
2274 ]);
2275 }
2276 if (debugCheckIntrinsicSizes) {
2277 // verify that the intrinsics are sane
2278 assert(!RenderObject.debugCheckingIntrinsics);
2279 RenderObject.debugCheckingIntrinsics = true;
2280 final List<DiagnosticsNode> failures = <DiagnosticsNode>[];
2281
2282 double testIntrinsic(double Function(double extent) function, String name, double constraint) {
2283 final double result = function(constraint);
2284 if (result < 0) {
2285 failures.add(ErrorDescription(' * $name($constraint) returned a negative value: $result'));
2286 }
2287 if (!result.isFinite) {
2288 failures.add(ErrorDescription(' * $name($constraint) returned a non-finite value: $result'));
2289 }
2290 return result;
2291 }
2292
2293 void testIntrinsicsForValues(double Function(double extent) getMin, double Function(double extent) getMax, String name, double constraint) {
2294 final double min = testIntrinsic(getMin, 'getMinIntrinsic$name', constraint);
2295 final double max = testIntrinsic(getMax, 'getMaxIntrinsic$name', constraint);
2296 if (min > max) {
2297 failures.add(ErrorDescription(' * getMinIntrinsic$name($constraint) returned a larger value ($min) than getMaxIntrinsic$name($constraint) ($max)'));
2298 }
2299 }
2300
2301 testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', double.infinity);
2302 testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', double.infinity);
2303 if (constraints.hasBoundedWidth) {
2304 testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', constraints.maxHeight);
2305 }
2306 if (constraints.hasBoundedHeight) {
2307 testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', constraints.maxWidth);
2308 }
2309
2310 // TODO(ianh): Test that values are internally consistent in more ways than the above.
2311
2312 RenderObject.debugCheckingIntrinsics = false;
2313 if (failures.isNotEmpty) {
2314 // TODO(jacobr): consider nesting the failures object so it is collapsible.
2315 throw FlutterError.fromParts(<DiagnosticsNode>[
2316 ErrorSummary('The intrinsic dimension methods of the $runtimeType class returned values that violate the intrinsic protocol contract.'),
2317 ErrorDescription('The following ${failures.length > 1 ? "failures" : "failure"} was detected:'), // should this be tagged as an error or not?
2318 ...failures,
2319 ErrorHint(
2320 'If you are not writing your own RenderBox subclass, then this is not\n'
2321 'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.yml',
2322 ),
2323 ]);
2324 }
2325
2326 // Checking that getDryLayout computes the same size.
2327 _dryLayoutCalculationValid = true;
2328 RenderObject.debugCheckingIntrinsics = true;
2329 final Size dryLayoutSize;
2330 try {
2331 dryLayoutSize = getDryLayout(constraints);
2332 } finally {
2333 RenderObject.debugCheckingIntrinsics = false;
2334 }
2335 if (_dryLayoutCalculationValid && dryLayoutSize != size) {
2336 throw FlutterError.fromParts(<DiagnosticsNode>[
2337 ErrorSummary('The size given to the ${objectRuntimeType(this, 'RenderBox')} class differs from the size computed by computeDryLayout.'),
2338 ErrorDescription(
2339 'The size computed in ${sizedByParent ? 'performResize' : 'performLayout'} '
2340 'is $size, which is different from $dryLayoutSize, which was computed by computeDryLayout.',
2341 ),
2342 ErrorDescription(
2343 'The constraints used were $constraints.',
2344 ),
2345 ErrorHint(
2346 'If you are not writing your own RenderBox subclass, then this is not\n'
2347 'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.yml',
2348 ),
2349 ]);
2350 }
2351 }
2352 return true;
2353 }());
2354 }
2355
2356 bool _clearCachedData() {
2357 if ((_cachedBaselines != null && _cachedBaselines!.isNotEmpty) ||
2358 (_cachedIntrinsicDimensions != null && _cachedIntrinsicDimensions!.isNotEmpty) ||
2359 (_cachedDryLayoutSizes != null && _cachedDryLayoutSizes!.isNotEmpty)) {
2360 // If we have cached data, then someone must have used our data.
2361 // Since the parent will shortly be marked dirty, we can forget that they
2362 // used the baseline and/or intrinsic dimensions. If they use them again,
2363 // then we'll fill the cache again, and if we get dirty again, we'll
2364 // notify them again.
2365 _cachedBaselines?.clear();
2366 _cachedIntrinsicDimensions?.clear();
2367 _cachedDryLayoutSizes?.clear();
2368 return true;
2369 }
2370 return false;
2371 }
2372
2373 @override
2374 void markNeedsLayout() {
2375 if (_clearCachedData() && parent is RenderObject) {
2376 markParentNeedsLayout();
2377 return;
2378 }
2379 super.markNeedsLayout();
2380 }
2381
2382 @override
2383 void layout(Constraints constraints, {bool parentUsesSize = false}) {
2384 if (hasSize && constraints != this.constraints &&
2385 _cachedBaselines != null && _cachedBaselines!.isNotEmpty) {
2386 // The cached baselines data may need update if the constraints change.
2387 _cachedBaselines?.clear();
2388 }
2389 super.layout(constraints, parentUsesSize: parentUsesSize);
2390 }
2391
2392 /// {@macro flutter.rendering.RenderObject.performResize}
2393 ///
2394 /// By default this method sets [size] to the result of [computeDryLayout]
2395 /// called with the current [constraints]. Instead of overriding this method,
2396 /// consider overriding [computeDryLayout].
2397 @override
2398 void performResize() {
2399 // default behavior for subclasses that have sizedByParent = true
2400 size = computeDryLayout(constraints);
2401 assert(size.isFinite);
2402 }
2403
2404 @override
2405 void performLayout() {
2406 assert(() {
2407 if (!sizedByParent) {
2408 throw FlutterError.fromParts(<DiagnosticsNode>[
2409 ErrorSummary('$runtimeType did not implement performLayout().'),
2410 ErrorHint(
2411 'RenderBox subclasses need to either override performLayout() to '
2412 'set a size and lay out any children, or, set sizedByParent to true '
2413 'so that performResize() sizes the render object.',
2414 ),
2415 ]);
2416 }
2417 return true;
2418 }());
2419 }
2420
2421 /// Determines the set of render objects located at the given position.
2422 ///
2423 /// Returns true, and adds any render objects that contain the point to the
2424 /// given hit test result, if this render object or one of its descendants
2425 /// absorbs the hit (preventing objects below this one from being hit).
2426 /// Returns false if the hit can continue to other objects below this one.
2427 ///
2428 /// The caller is responsible for transforming [position] from global
2429 /// coordinates to its location relative to the origin of this [RenderBox].
2430 /// This [RenderBox] is responsible for checking whether the given position is
2431 /// within its bounds.
2432 ///
2433 /// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
2434 /// [BoxHitTestResult.addWithPaintOffset], or
2435 /// [BoxHitTestResult.addWithRawTransform] need to be invoked by the caller
2436 /// to record the required transform operations in the [HitTestResult]. These
2437 /// methods will also help with applying the transform to `position`.
2438 ///
2439 /// Hit testing requires layout to be up-to-date but does not require painting
2440 /// to be up-to-date. That means a render object can rely upon [performLayout]
2441 /// having been called in [hitTest] but cannot rely upon [paint] having been
2442 /// called. For example, a render object might be a child of a [RenderOpacity]
2443 /// object, which calls [hitTest] on its children when its opacity is zero
2444 /// even through it does not [paint] its children.
2445 bool hitTest(BoxHitTestResult result, { required Offset position }) {
2446 assert(() {
2447 if (!hasSize) {
2448 if (debugNeedsLayout) {
2449 throw FlutterError.fromParts(<DiagnosticsNode>[
2450 ErrorSummary('Cannot hit test a render box that has never been laid out.'),
2451 describeForError('The hitTest() method was called on this RenderBox'),
2452 ErrorDescription(
2453 "Unfortunately, this object's geometry is not known at this time, "
2454 'probably because it has never been laid out. '
2455 'This means it cannot be accurately hit-tested.',
2456 ),
2457 ErrorHint(
2458 'If you are trying '
2459 'to perform a hit test during the layout phase itself, make sure '
2460 "you only hit test nodes that have completed layout (e.g. the node's "
2461 'children, after their layout() method has been called).',
2462 ),
2463 ]);
2464 }
2465 throw FlutterError.fromParts(<DiagnosticsNode>[
2466 ErrorSummary('Cannot hit test a render box with no size.'),
2467 describeForError('The hitTest() method was called on this RenderBox'),
2468 ErrorDescription(
2469 'Although this node is not marked as needing layout, '
2470 'its size is not set.',
2471 ),
2472 ErrorHint(
2473 'A RenderBox object must have an '
2474 'explicit size before it can be hit-tested. Make sure '
2475 'that the RenderBox in question sets its size during layout.',
2476 ),
2477 ]);
2478 }
2479 return true;
2480 }());
2481 if (_size!.contains(position)) {
2482 if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
2483 result.add(BoxHitTestEntry(this, position));
2484 return true;
2485 }
2486 }
2487 return false;
2488 }
2489
2490 /// Override this method if this render object can be hit even if its
2491 /// children were not hit.
2492 ///
2493 /// Returns true if the specified `position` should be considered a hit
2494 /// on this render object.
2495 ///
2496 /// The caller is responsible for transforming [position] from global
2497 /// coordinates to its location relative to the origin of this [RenderBox].
2498 /// This [RenderBox] is responsible for checking whether the given position is
2499 /// within its bounds.
2500 ///
2501 /// Used by [hitTest]. If you override [hitTest] and do not call this
2502 /// function, then you don't need to implement this function.
2503 @protected
2504 bool hitTestSelf(Offset position) => false;
2505
2506 /// Override this method to check whether any children are located at the
2507 /// given position.
2508 ///
2509 /// Subclasses should return true if at least one child reported a hit at the
2510 /// specified position.
2511 ///
2512 /// Typically children should be hit-tested in reverse paint order so that
2513 /// hit tests at locations where children overlap hit the child that is
2514 /// visually "on top" (i.e., paints later).
2515 ///
2516 /// The caller is responsible for transforming [position] from global
2517 /// coordinates to its location relative to the origin of this [RenderBox].
2518 /// Likewise, this [RenderBox] is responsible for transforming the position
2519 /// that it passes to its children when it calls [hitTest] on each child.
2520 ///
2521 /// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
2522 /// [BoxHitTestResult.addWithPaintOffset], or
2523 /// [BoxHitTestResult.addWithRawTransform] need to be invoked by subclasses to
2524 /// record the required transform operations in the [BoxHitTestResult]. These
2525 /// methods will also help with applying the transform to `position`.
2526 ///
2527 /// Used by [hitTest]. If you override [hitTest] and do not call this
2528 /// function, then you don't need to implement this function.
2529 @protected
2530 bool hitTestChildren(BoxHitTestResult result, { required Offset position }) => false;
2531
2532 /// Multiply the transform from the parent's coordinate system to this box's
2533 /// coordinate system into the given transform.
2534 ///
2535 /// This function is used to convert coordinate systems between boxes.
2536 /// Subclasses that apply transforms during painting should override this
2537 /// function to factor those transforms into the calculation.
2538 ///
2539 /// The [RenderBox] implementation takes care of adjusting the matrix for the
2540 /// position of the given child as determined during layout and stored on the
2541 /// child's [parentData] in the [BoxParentData.offset] field.
2542 @override
2543 void applyPaintTransform(RenderObject child, Matrix4 transform) {
2544 assert(child.parent == this);
2545 assert(() {
2546 if (child.parentData is! BoxParentData) {
2547 throw FlutterError.fromParts(<DiagnosticsNode>[
2548 ErrorSummary('$runtimeType does not implement applyPaintTransform.'),
2549 describeForError('The following $runtimeType object'),
2550 child.describeForError('...did not use a BoxParentData class for the parentData field of the following child'),
2551 ErrorDescription('The $runtimeType class inherits from RenderBox.'),
2552 ErrorHint(
2553 'The default applyPaintTransform implementation provided by RenderBox assumes that the '
2554 'children all use BoxParentData objects for their parentData field. '
2555 'Since $runtimeType does not in fact use that ParentData class for its children, it must '
2556 'provide an implementation of applyPaintTransform that supports the specific ParentData '
2557 'subclass used by its children (which apparently is ${child.parentData.runtimeType}).',
2558 ),
2559 ]);
2560 }
2561 return true;
2562 }());
2563 final BoxParentData childParentData = child.parentData! as BoxParentData;
2564 final Offset offset = childParentData.offset;
2565 transform.translate(offset.dx, offset.dy);
2566 }
2567
2568 /// Convert the given point from the global coordinate system in logical pixels
2569 /// to the local coordinate system for this box.
2570 ///
2571 /// This method will un-project the point from the screen onto the widget,
2572 /// which makes it different from [MatrixUtils.transformPoint].
2573 ///
2574 /// If the transform from global coordinates to local coordinates is
2575 /// degenerate, this function returns [Offset.zero].
2576 ///
2577 /// If `ancestor` is non-null, this function converts the given point from the
2578 /// coordinate system of `ancestor` (which must be an ancestor of this render
2579 /// object) instead of from the global coordinate system.
2580 ///
2581 /// This method is implemented in terms of [getTransformTo].
2582 Offset globalToLocal(Offset point, { RenderObject? ancestor }) {
2583 // We want to find point (p) that corresponds to a given point on the
2584 // screen (s), but that also physically resides on the local render plane,
2585 // so that it is useful for visually accurate gesture processing in the
2586 // local space. For that, we can't simply transform 2D screen point to
2587 // the 3D local space since the screen space lacks the depth component |z|,
2588 // and so there are many 3D points that correspond to the screen point.
2589 // We must first unproject the screen point onto the render plane to find
2590 // the true 3D point that corresponds to the screen point.
2591 // We do orthogonal unprojection after undoing perspective, in local space.
2592 // The render plane is specified by renderBox offset (o) and Z axis (n).
2593 // Unprojection is done by finding the intersection of the view vector (d)
2594 // with the local X-Y plane: (o-s).dot(n) == (p-s).dot(n), (p-s) == |z|*d.
2595 final Matrix4 transform = getTransformTo(ancestor);
2596 final double det = transform.invert();
2597 if (det == 0.0) {
2598 return Offset.zero;
2599 }
2600 final Vector3 n = Vector3(0.0, 0.0, 1.0);
2601 final Vector3 i = transform.perspectiveTransform(Vector3(0.0, 0.0, 0.0));
2602 final Vector3 d = transform.perspectiveTransform(Vector3(0.0, 0.0, 1.0)) - i;
2603 final Vector3 s = transform.perspectiveTransform(Vector3(point.dx, point.dy, 0.0));
2604 final Vector3 p = s - d * (n.dot(s) / n.dot(d));
2605 return Offset(p.x, p.y);
2606 }
2607
2608 /// Convert the given point from the local coordinate system for this box to
2609 /// the global coordinate system in logical pixels.
2610 ///
2611 /// If `ancestor` is non-null, this function converts the given point to the
2612 /// coordinate system of `ancestor` (which must be an ancestor of this render
2613 /// object) instead of to the global coordinate system.
2614 ///
2615 /// This method is implemented in terms of [getTransformTo]. If the transform
2616 /// matrix puts the given `point` on the line at infinity (for instance, when
2617 /// the transform matrix is the zero matrix), this method returns (NaN, NaN).
2618 Offset localToGlobal(Offset point, { RenderObject? ancestor }) {
2619 return MatrixUtils.transformPoint(getTransformTo(ancestor), point);
2620 }
2621
2622 /// Returns a rectangle that contains all the pixels painted by this box.
2623 ///
2624 /// The paint bounds can be larger or smaller than [size], which is the amount
2625 /// of space this box takes up during layout. For example, if this box casts a
2626 /// shadow, that shadow might extend beyond the space allocated to this box
2627 /// during layout.
2628 ///
2629 /// The paint bounds are used to size the buffers into which this box paints.
2630 /// If the box attempts to paints outside its paint bounds, there might not be
2631 /// enough memory allocated to represent the box's visual appearance, which
2632 /// can lead to undefined behavior.
2633 ///
2634 /// The returned paint bounds are in the local coordinate system of this box.
2635 @override
2636 Rect get paintBounds => Offset.zero & size;
2637
2638 /// Override this method to handle pointer events that hit this render object.
2639 ///
2640 /// For [RenderBox] objects, the `entry` argument is a [BoxHitTestEntry]. From this
2641 /// object you can determine the [PointerDownEvent]'s position in local coordinates.
2642 /// (This is useful because [PointerEvent.position] is in global coordinates.)
2643 ///
2644 /// Implementations of this method should call [debugHandleEvent] as follows,
2645 /// so that they support [debugPaintPointersEnabled]:
2646 ///
2647 /// ```dart
2648 /// class RenderFoo extends RenderBox {
2649 /// // ...
2650 ///
2651 /// @override
2652 /// void handleEvent(PointerEvent event, HitTestEntry entry) {
2653 /// assert(debugHandleEvent(event, entry));
2654 /// // ... handle the event ...
2655 /// }
2656 ///
2657 /// // ...
2658 /// }
2659 /// ```
2660 @override
2661 void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
2662 super.handleEvent(event, entry);
2663 }
2664
2665 int _debugActivePointers = 0;
2666
2667 /// Implements the [debugPaintPointersEnabled] debugging feature.
2668 ///
2669 /// [RenderBox] subclasses that implement [handleEvent] should call
2670 /// [debugHandleEvent] from their [handleEvent] method, as follows:
2671 ///
2672 /// ```dart
2673 /// class RenderFoo extends RenderBox {
2674 /// // ...
2675 ///
2676 /// @override
2677 /// void handleEvent(PointerEvent event, HitTestEntry entry) {
2678 /// assert(debugHandleEvent(event, entry));
2679 /// // ... handle the event ...
2680 /// }
2681 ///
2682 /// // ...
2683 /// }
2684 /// ```
2685 ///
2686 /// If you call this for a [PointerDownEvent], make sure you also call it for
2687 /// the corresponding [PointerUpEvent] or [PointerCancelEvent].
2688 bool debugHandleEvent(PointerEvent event, HitTestEntry entry) {
2689 assert(() {
2690 if (debugPaintPointersEnabled) {
2691 if (event is PointerDownEvent) {
2692 _debugActivePointers += 1;
2693 } else if (event is PointerUpEvent || event is PointerCancelEvent) {
2694 _debugActivePointers -= 1;
2695 }
2696 markNeedsPaint();
2697 }
2698 return true;
2699 }());
2700 return true;
2701 }
2702
2703 @override
2704 void debugPaint(PaintingContext context, Offset offset) {
2705 assert(() {
2706 if (debugPaintSizeEnabled) {
2707 debugPaintSize(context, offset);
2708 }
2709 if (debugPaintBaselinesEnabled) {
2710 debugPaintBaselines(context, offset);
2711 }
2712 if (debugPaintPointersEnabled) {
2713 debugPaintPointers(context, offset);
2714 }
2715 return true;
2716 }());
2717 }
2718
2719 /// In debug mode, paints a border around this render box.
2720 ///
2721 /// Called for every [RenderBox] when [debugPaintSizeEnabled] is true.
2722 @protected
2723 @visibleForTesting
2724 void debugPaintSize(PaintingContext context, Offset offset) {
2725 assert(() {
2726 final Paint paint = Paint()
2727 ..style = PaintingStyle.stroke
2728 ..strokeWidth = 1.0
2729 ..color = const Color(0xFF00FFFF);
2730 context.canvas.drawRect((offset & size).deflate(0.5), paint);
2731 return true;
2732 }());
2733 }
2734
2735 /// In debug mode, paints a line for each baseline.
2736 ///
2737 /// Called for every [RenderBox] when [debugPaintBaselinesEnabled] is true.
2738 @protected
2739 void debugPaintBaselines(PaintingContext context, Offset offset) {
2740 assert(() {
2741 final Paint paint = Paint()
2742 ..style = PaintingStyle.stroke
2743 ..strokeWidth = 0.25;
2744 Path path;
2745 // ideographic baseline
2746 final double? baselineI = getDistanceToBaseline(TextBaseline.ideographic, onlyReal: true);
2747 if (baselineI != null) {
2748 paint.color = const Color(0xFFFFD000);
2749 path = Path();
2750 path.moveTo(offset.dx, offset.dy + baselineI);
2751 path.lineTo(offset.dx + size.width, offset.dy + baselineI);
2752 context.canvas.drawPath(path, paint);
2753 }
2754 // alphabetic baseline
2755 final double? baselineA = getDistanceToBaseline(TextBaseline.alphabetic, onlyReal: true);
2756 if (baselineA != null) {
2757 paint.color = const Color(0xFF00FF00);
2758 path = Path();
2759 path.moveTo(offset.dx, offset.dy + baselineA);
2760 path.lineTo(offset.dx + size.width, offset.dy + baselineA);
2761 context.canvas.drawPath(path, paint);
2762 }
2763 return true;
2764 }());
2765 }
2766
2767 /// In debug mode, paints a rectangle if this render box has counted more
2768 /// pointer downs than pointer up events.
2769 ///
2770 /// Called for every [RenderBox] when [debugPaintPointersEnabled] is true.
2771 ///
2772 /// By default, events are not counted. For details on how to ensure that
2773 /// events are counted for your class, see [debugHandleEvent].
2774 @protected
2775 void debugPaintPointers(PaintingContext context, Offset offset) {
2776 assert(() {
2777 if (_debugActivePointers > 0) {
2778 final Paint paint = Paint()
2779 ..color = Color(0x00BBBB | ((0x04000000 * depth) & 0xFF000000));
2780 context.canvas.drawRect(offset & size, paint);
2781 }
2782 return true;
2783 }());
2784 }
2785
2786 @override
2787 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2788 super.debugFillProperties(properties);
2789 properties.add(DiagnosticsProperty<Size>('size', _size, missingIfNull: true));
2790 }
2791}
2792
2793/// A mixin that provides useful default behaviors for boxes with children
2794/// managed by the [ContainerRenderObjectMixin] mixin.
2795///
2796/// By convention, this class doesn't override any members of the superclass.
2797/// Instead, it provides helpful functions that subclasses can call as
2798/// appropriate.
2799mixin RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, ParentDataType extends ContainerBoxParentData<ChildType>> implements ContainerRenderObjectMixin<ChildType, ParentDataType> {
2800 /// Returns the baseline of the first child with a baseline.
2801 ///
2802 /// Useful when the children are displayed vertically in the same order they
2803 /// appear in the child list.
2804 double? defaultComputeDistanceToFirstActualBaseline(TextBaseline baseline) {
2805 assert(!debugNeedsLayout);
2806 ChildType? child = firstChild;
2807 while (child != null) {
2808 final ParentDataType? childParentData = child.parentData as ParentDataType?;
2809 final double? result = child.getDistanceToActualBaseline(baseline);
2810 if (result != null) {
2811 return result + childParentData!.offset.dy;
2812 }
2813 child = childParentData!.nextSibling;
2814 }
2815 return null;
2816 }
2817
2818 /// Returns the minimum baseline value among every child.
2819 ///
2820 /// Useful when the vertical position of the children isn't determined by the
2821 /// order in the child list.
2822 double? defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline) {
2823 assert(!debugNeedsLayout);
2824 double? result;
2825 ChildType? child = firstChild;
2826 while (child != null) {
2827 final ParentDataType childParentData = child.parentData! as ParentDataType;
2828 double? candidate = child.getDistanceToActualBaseline(baseline);
2829 if (candidate != null) {
2830 candidate += childParentData.offset.dy;
2831 if (result != null) {
2832 result = math.min(result, candidate);
2833 } else {
2834 result = candidate;
2835 }
2836 }
2837 child = childParentData.nextSibling;
2838 }
2839 return result;
2840 }
2841
2842 /// Performs a hit test on each child by walking the child list backwards.
2843 ///
2844 /// Stops walking once after the first child reports that it contains the
2845 /// given point. Returns whether any children contain the given point.
2846 ///
2847 /// See also:
2848 ///
2849 /// * [defaultPaint], which paints the children appropriate for this
2850 /// hit-testing strategy.
2851 bool defaultHitTestChildren(BoxHitTestResult result, { required Offset position }) {
2852 ChildType? child = lastChild;
2853 while (child != null) {
2854 // The x, y parameters have the top left of the node's box as the origin.
2855 final ParentDataType childParentData = child.parentData! as ParentDataType;
2856 final bool isHit = result.addWithPaintOffset(
2857 offset: childParentData.offset,
2858 position: position,
2859 hitTest: (BoxHitTestResult result, Offset transformed) {
2860 assert(transformed == position - childParentData.offset);
2861 return child!.hitTest(result, position: transformed);
2862 },
2863 );
2864 if (isHit) {
2865 return true;
2866 }
2867 child = childParentData.previousSibling;
2868 }
2869 return false;
2870 }
2871
2872 /// Paints each child by walking the child list forwards.
2873 ///
2874 /// See also:
2875 ///
2876 /// * [defaultHitTestChildren], which implements hit-testing of the children
2877 /// in a manner appropriate for this painting strategy.
2878 void defaultPaint(PaintingContext context, Offset offset) {
2879 ChildType? child = firstChild;
2880 while (child != null) {
2881 final ParentDataType childParentData = child.parentData! as ParentDataType;
2882 context.paintChild(child, childParentData.offset + offset);
2883 child = childParentData.nextSibling;
2884 }
2885 }
2886
2887 /// Returns a list containing the children of this render object.
2888 ///
2889 /// This function is useful when you need random-access to the children of
2890 /// this render object. If you're accessing the children in order, consider
2891 /// walking the child list directly.
2892 List<ChildType> getChildrenAsList() {
2893 final List<ChildType> result = <ChildType>[];
2894 RenderBox? child = firstChild;
2895 while (child != null) {
2896 final ParentDataType childParentData = child.parentData! as ParentDataType;
2897 result.add(child as ChildType);
2898 child = childParentData.nextSibling;
2899 }
2900 return result;
2901 }
2902}
2903