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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com