box.dart 119 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;
6
import 'dart:ui' as ui show lerpDouble;
7

8
import 'package:flutter/foundation.dart';
9
import 'package:flutter/gestures.dart';
10

11
import 'package:vector_math/vector_math_64.dart';
12

13 14 15
import 'debug.dart';
import 'object.dart';

16
// This class should only be used in debug builds.
17
class _DebugSize extends Size {
18
  _DebugSize(Size source, this._owner, this._canBeUsedByParent) : super.copy(source);
19
  final RenderBox _owner;
20
  final bool _canBeUsedByParent;
21 22
}

23
/// Immutable layout constraints for [RenderBox] layout.
24
///
25
/// A [Size] respects a [BoxConstraints] if, and only if, all of the following
26 27
/// relations hold:
///
28 29
/// * [minWidth] <= [Size.width] <= [maxWidth]
/// * [minHeight] <= [Size.height] <= [maxHeight]
30 31 32
///
/// The constraints themselves must satisfy these relations:
///
33 34
/// * 0.0 <= [minWidth] <= [maxWidth] <= [double.infinity]
/// * 0.0 <= [minHeight] <= [maxHeight] <= [double.infinity]
35
///
36
/// [double.infinity] is a legal value for each constraint.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
///
/// ## The box layout model
///
/// Render objects in the Flutter framework are laid out by a one-pass layout
/// model which walks down the render tree passing constraints, then walks back
/// up the render tree passing concrete geometry.
///
/// For boxes, the constraints are [BoxConstraints], which, as described herein,
/// consist of four numbers: a minimum width [minWidth], a maximum width
/// [maxWidth], a minimum height [minHeight], and a maximum height [maxHeight].
///
/// The geometry for boxes consists of a [Size], which must satisfy the
/// constraints described above.
///
/// Each [RenderBox] (the objects that provide the layout models for box
/// widgets) receives [BoxConstraints] from its parent, then lays out each of
/// its children, then picks a [Size] that satisfies the [BoxConstraints].
///
/// Render objects position their children independently of laying them out.
/// Frequently, the parent will use the children's sizes to determine their
/// position. A child does not know its position and will not necessarily be
/// laid out again, or repainted, if its position changes.
///
/// ## Terminology
///
/// When the minimum constraints and the maximum constraint in an axis are the
/// same, that axis is _tightly_ constrained. See: [new
/// BoxConstraints.tightFor], [new BoxConstraints.tightForFinite], [tighten],
/// [hasTightWidth], [hasTightHeight], [isTight].
///
/// An axis with a minimum constraint of 0.0 is _loose_ (regardless of the
/// maximum constraint; if it is also 0.0, then the axis is simultaneously tight
/// and loose!). See: [new BoxConstraints.loose], [loosen].
///
/// An axis whose maximum constraint is not infinite is _bounded_. See:
/// [hasBoundedWidth], [hasBoundedHeight].
///
/// An axis whose maximum constraint is infinite is _unbounded_. An axis is
/// _expanding_ if it is tightly infinite (its minimum and maximum constraints
/// are both infinite). See: [new BoxConstraints.expand].
///
78 79 80 81
/// An axis whose _minimum_ constraint is infinite is just said to be _infinite_
/// (since by definition the maximum constraint must also be infinite in that
/// case). See: [hasInfiniteWidth], [hasInfiniteHeight].
///
82 83 84 85
/// A size is _constrained_ when it satisfies a [BoxConstraints] description.
/// See: [constrain], [constrainWidth], [constrainHeight],
/// [constrainDimensions], [constrainSizeAndAttemptToPreserveAspectRatio],
/// [isSatisfiedBy].
86
class BoxConstraints extends Constraints {
87
  /// Creates box constraints with the given constraints.
88
  const BoxConstraints({
89 90 91
    this.minWidth = 0.0,
    this.maxWidth = double.infinity,
    this.minHeight = 0.0,
92
    this.maxHeight = double.infinity,
93 94 95 96
  }) : assert(minWidth != null),
       assert(maxWidth != null),
       assert(minHeight != null),
       assert(maxHeight != null);
97

98
  /// Creates box constraints that is respected only by the given size.
99 100 101 102 103 104
  BoxConstraints.tight(Size size)
    : minWidth = size.width,
      maxWidth = size.width,
      minHeight = size.height,
      maxHeight = size.height;

105
  /// Creates box constraints that require the given width or height.
106 107 108 109 110 111
  ///
  /// See also:
  ///
  ///  * [new BoxConstraints.tightForFinite], which is similar but instead of
  ///    being tight if the value is non-null, is tight if the value is not
  ///    infinite.
112
  const BoxConstraints.tightFor({
113 114
    double? width,
    double? height,
115 116 117 118
  }) : minWidth = width ?? 0.0,
       maxWidth = width ?? double.infinity,
       minHeight = height ?? 0.0,
       maxHeight = height ?? double.infinity;
119

120 121 122 123 124 125 126
  /// Creates box constraints that require the given width or height, except if
  /// they are infinite.
  ///
  /// See also:
  ///
  ///  * [new BoxConstraints.tightFor], which is similar but instead of being
  ///    tight if the value is not infinite, is tight if the value is non-null.
127
  const BoxConstraints.tightForFinite({
128
    double width = double.infinity,
129
    double height = double.infinity,
130 131 132 133
  }) : minWidth = width != double.infinity ? width : 0.0,
       maxWidth = width != double.infinity ? width : double.infinity,
       minHeight = height != double.infinity ? height : 0.0,
       maxHeight = height != double.infinity ? height : double.infinity;
134

135
  /// Creates box constraints that forbid sizes larger than the given size.
136 137 138 139 140 141
  BoxConstraints.loose(Size size)
    : minWidth = 0.0,
      maxWidth = size.width,
      minHeight = 0.0,
      maxHeight = size.height;

142
  /// Creates box constraints that expand to fill another box constraints.
143 144 145 146
  ///
  /// If width or height is given, the constraints will require exactly the
  /// given value in the given dimension.
  const BoxConstraints.expand({
147 148
    double? width,
    double? height,
149 150 151 152
  }) : minWidth = width ?? double.infinity,
       maxWidth = width ?? double.infinity,
       minHeight = height ?? double.infinity,
       maxHeight = height ?? double.infinity;
153

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  /// The minimum width that satisfies the constraints.
  final double minWidth;

  /// The maximum width that satisfies the constraints.
  ///
  /// Might be [double.infinity].
  final double maxWidth;

  /// The minimum height that satisfies the constraints.
  final double minHeight;

  /// The maximum height that satisfies the constraints.
  ///
  /// Might be [double.infinity].
  final double maxHeight;

170
  /// Creates a copy of this box constraints but with the given fields replaced with the new values.
Hixie's avatar
Hixie committed
171
  BoxConstraints copyWith({
172 173 174 175
    double? minWidth,
    double? maxWidth,
    double? minHeight,
    double? maxHeight,
Hixie's avatar
Hixie committed
176
  }) {
177
    return BoxConstraints(
Hixie's avatar
Hixie committed
178 179 180
      minWidth: minWidth ?? this.minWidth,
      maxWidth: maxWidth ?? this.maxWidth,
      minHeight: minHeight ?? this.minHeight,
181
      maxHeight: maxHeight ?? this.maxHeight,
Hixie's avatar
Hixie committed
182 183 184
    );
  }

185
  /// Returns new box constraints that are smaller by the given edge dimensions.
186
  BoxConstraints deflate(EdgeInsets edges) {
187
    assert(edges != null);
188
    assert(debugAssertIsValid());
189 190 191 192
    final double horizontal = edges.horizontal;
    final double vertical = edges.vertical;
    final double deflatedMinWidth = math.max(0.0, minWidth - horizontal);
    final double deflatedMinHeight = math.max(0.0, minHeight - vertical);
193
    return BoxConstraints(
194 195 196
      minWidth: deflatedMinWidth,
      maxWidth: math.max(deflatedMinWidth, maxWidth - horizontal),
      minHeight: deflatedMinHeight,
197
      maxHeight: math.max(deflatedMinHeight, maxHeight - vertical),
198 199 200
    );
  }

201
  /// Returns new box constraints that remove the minimum width and height requirements.
202
  BoxConstraints loosen() {
203
    assert(debugAssertIsValid());
204
    return BoxConstraints(
205
      maxWidth: maxWidth,
206
      maxHeight: maxHeight,
207 208 209
    );
  }

210 211
  /// Returns new box constraints that respect the given constraints while being
  /// as close as possible to the original constraints.
212
  BoxConstraints enforce(BoxConstraints constraints) {
213
    return BoxConstraints(
214 215 216 217
      minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
      maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
      minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
      maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight),
218 219 220
    );
  }

221 222 223
  /// Returns new box constraints with a tight width and/or height as close to
  /// the given width and height as possible while still respecting the original
  /// box constraints.
224
  BoxConstraints tighten({ double? width, double? height }) {
225
    return BoxConstraints(
226 227 228 229
      minWidth: width == null ? minWidth : width.clamp(minWidth, maxWidth),
      maxWidth: width == null ? maxWidth : width.clamp(minWidth, maxWidth),
      minHeight: height == null ? minHeight : height.clamp(minHeight, maxHeight),
      maxHeight: height == null ? maxHeight : height.clamp(minHeight, maxHeight),
230
    );
231 232
  }

233 234
  /// A box constraints with the width and height constraints flipped.
  BoxConstraints get flipped {
235
    return BoxConstraints(
236 237 238
      minWidth: minHeight,
      maxWidth: maxHeight,
      minHeight: minWidth,
239
      maxHeight: maxWidth,
240 241 242
    );
  }

243
  /// Returns box constraints with the same width constraints but with
244
  /// unconstrained height.
245
  BoxConstraints widthConstraints() => BoxConstraints(minWidth: minWidth, maxWidth: maxWidth);
246

247
  /// Returns box constraints with the same height constraints but with
248
  /// unconstrained width.
249
  BoxConstraints heightConstraints() => BoxConstraints(minHeight: minHeight, maxHeight: maxHeight);
250

251 252
  /// Returns the width that both satisfies the constraints and is as close as
  /// possible to the given width.
253
  double constrainWidth([ double width = double.infinity ]) {
254
    assert(debugAssertIsValid());
255
    return width.clamp(minWidth, maxWidth);
256 257
  }

258 259
  /// Returns the height that both satisfies the constraints and is as close as
  /// possible to the given height.
260
  double constrainHeight([ double height = double.infinity ]) {
261
    assert(debugAssertIsValid());
262
    return height.clamp(minHeight, maxHeight);
263 264
  }

265
  Size _debugPropagateDebugSize(Size size, Size result) {
266 267
    assert(() {
      if (size is _DebugSize)
268
        result = _DebugSize(result, size._owner, size._canBeUsedByParent);
269
      return true;
270
    }());
271 272
    return result;
  }
273

274 275 276
  /// Returns the size that both satisfies the constraints and is as close as
  /// possible to the given size.
  ///
277 278 279 280
  /// See also:
  ///
  ///  * [constrainDimensions], which applies the same algorithm to
  ///    separately provided widths and heights.
281
  Size constrain(Size size) {
282
    Size result = Size(constrainWidth(size.width), constrainHeight(size.height));
283 284 285 286
    assert(() {
      result = _debugPropagateDebugSize(size, result);
      return true;
    }());
287 288 289 290 291 292 293 294 295
    return result;
  }

  /// Returns the size that both satisfies the constraints and is as close as
  /// possible to the given width and height.
  ///
  /// When you already have a [Size], prefer [constrain], which applies the same
  /// algorithm to a [Size] directly.
  Size constrainDimensions(double width, double height) {
296
    return Size(constrainWidth(width), constrainHeight(height));
297 298
  }

Adam Barth's avatar
Adam Barth committed
299 300
  /// Returns a size that attempts to meet the following conditions, in order:
  ///
301 302
  ///  * The size must satisfy these constraints.
  ///  * The aspect ratio of the returned size matches the aspect ratio of the
Adam Barth's avatar
Adam Barth committed
303
  ///    given size.
304
  ///  * The returned size as big as possible while still being equal to or
Adam Barth's avatar
Adam Barth committed
305 306
  ///    smaller than the given size.
  Size constrainSizeAndAttemptToPreserveAspectRatio(Size size) {
307 308
    if (isTight) {
      Size result = smallest;
309 310 311 312
      assert(() {
        result = _debugPropagateDebugSize(size, result);
        return true;
      }());
313 314
      return result;
    }
Adam Barth's avatar
Adam Barth committed
315 316 317 318 319

    double width = size.width;
    double height = size.height;
    assert(width > 0.0);
    assert(height > 0.0);
320
    final double aspectRatio = width / height;
Adam Barth's avatar
Adam Barth committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

    if (width > maxWidth) {
      width = maxWidth;
      height = width / aspectRatio;
    }

    if (height > maxHeight) {
      height = maxHeight;
      width = height * aspectRatio;
    }

    if (width < minWidth) {
      width = minWidth;
      height = width / aspectRatio;
    }

    if (height < minHeight) {
      height = minHeight;
      width = height * aspectRatio;
    }

342
    Size result = Size(constrainWidth(width), constrainHeight(height));
343 344 345 346
    assert(() {
      result = _debugPropagateDebugSize(size, result);
      return true;
    }());
347
    return result;
Adam Barth's avatar
Adam Barth committed
348 349
  }

350
  /// The biggest size that satisfies the constraints.
351
  Size get biggest => Size(constrainWidth(), constrainHeight());
352

353
  /// The smallest size that satisfies the constraints.
354
  Size get smallest => Size(constrainWidth(0.0), constrainHeight(0.0));
355

356
  /// Whether there is exactly one width value that satisfies the constraints.
357
  bool get hasTightWidth => minWidth >= maxWidth;
358

359
  /// Whether there is exactly one height value that satisfies the constraints.
360
  bool get hasTightHeight => minHeight >= maxHeight;
361

362
  /// Whether there is exactly one size that satisfies the constraints.
363
  @override
364 365
  bool get isTight => hasTightWidth && hasTightHeight;

366
  /// Whether there is an upper bound on the maximum width.
367 368 369 370 371 372
  ///
  /// See also:
  ///
  ///  * [hasBoundedHeight], the equivalent for the vertical axis.
  ///  * [hasInfiniteWidth], which describes whether the minimum width
  ///    constraint is infinite.
373
  bool get hasBoundedWidth => maxWidth < double.infinity;
374 375

  /// Whether there is an upper bound on the maximum height.
376 377 378 379 380 381
  ///
  /// See also:
  ///
  ///  * [hasBoundedWidth], the equivalent for the horizontal axis.
  ///  * [hasInfiniteHeight], which describes whether the minimum height
  ///    constraint is infinite.
382
  bool get hasBoundedHeight => maxHeight < double.infinity;
383

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
  /// Whether the width constraint is infinite.
  ///
  /// Such a constraint is used to indicate that a box should grow as large as
  /// some other constraint (in this case, horizontally). If constraints are
  /// infinite, then they must have other (non-infinite) constraints [enforce]d
  /// upon them, or must be [tighten]ed, before they can be used to derive a
  /// [Size] for a [RenderBox.size].
  ///
  /// See also:
  ///
  ///  * [hasInfiniteHeight], the equivalent for the vertical axis.
  ///  * [hasBoundedWidth], which describes whether the maximum width
  ///    constraint is finite.
  bool get hasInfiniteWidth => minWidth >= double.infinity;

  /// Whether the height constraint is infinite.
  ///
  /// Such a constraint is used to indicate that a box should grow as large as
  /// some other constraint (in this case, vertically). If constraints are
  /// infinite, then they must have other (non-infinite) constraints [enforce]d
  /// upon them, or must be [tighten]ed, before they can be used to derive a
  /// [Size] for a [RenderBox.size].
  ///
  /// See also:
  ///
  ///  * [hasInfiniteWidth], the equivalent for the horizontal axis.
  ///  * [hasBoundedHeight], which describes whether the maximum height
  ///    constraint is finite.
  bool get hasInfiniteHeight => minHeight >= double.infinity;

414
  /// Whether the given size satisfies the constraints.
415
  bool isSatisfiedBy(Size size) {
416
    assert(debugAssertIsValid());
417 418
    return (minWidth <= size.width) && (size.width <= maxWidth) &&
           (minHeight <= size.height) && (size.height <= maxHeight);
419 420
  }

421 422
  /// Scales each constraint parameter by the given factor.
  BoxConstraints operator*(double factor) {
423
    return BoxConstraints(
424 425 426
      minWidth: minWidth * factor,
      maxWidth: maxWidth * factor,
      minHeight: minHeight * factor,
427
      maxHeight: maxHeight * factor,
Adam Barth's avatar
Adam Barth committed
428 429 430
    );
  }

431 432
  /// Scales each constraint parameter by the inverse of the given factor.
  BoxConstraints operator/(double factor) {
433
    return BoxConstraints(
434 435 436
      minWidth: minWidth / factor,
      maxWidth: maxWidth / factor,
      minHeight: minHeight / factor,
437
      maxHeight: maxHeight / factor,
Adam Barth's avatar
Adam Barth committed
438 439 440
    );
  }

441 442
  /// Scales each constraint parameter by the inverse of the given factor, rounded to the nearest integer.
  BoxConstraints operator~/(double factor) {
443
    return BoxConstraints(
444 445 446
      minWidth: (minWidth ~/ factor).toDouble(),
      maxWidth: (maxWidth ~/ factor).toDouble(),
      minHeight: (minHeight ~/ factor).toDouble(),
447
      maxHeight: (maxHeight ~/ factor).toDouble(),
Adam Barth's avatar
Adam Barth committed
448 449 450
    );
  }

451 452
  /// Computes the remainder of each constraint parameter by the given value.
  BoxConstraints operator%(double value) {
453
    return BoxConstraints(
454 455 456
      minWidth: minWidth % value,
      maxWidth: maxWidth % value,
      minHeight: minHeight % value,
457
      maxHeight: maxHeight % value,
Adam Barth's avatar
Adam Barth committed
458 459 460
    );
  }

461
  /// Linearly interpolate between two BoxConstraints.
Adam Barth's avatar
Adam Barth committed
462
  ///
463 464
  /// If either is null, this function interpolates from a [BoxConstraints]
  /// object whose fields are all set to 0.0.
465
  ///
466
  /// {@macro dart.ui.shadow.lerp}
467
  static BoxConstraints? lerp(BoxConstraints? a, BoxConstraints? b, double t) {
468
    assert(t != null);
Adam Barth's avatar
Adam Barth committed
469 470 471
    if (a == null && b == null)
      return null;
    if (a == null)
472
      return b! * t;
Adam Barth's avatar
Adam Barth committed
473 474
    if (b == null)
      return a * (1.0 - t);
475 476
    assert(a.debugAssertIsValid());
    assert(b.debugAssertIsValid());
477 478 479 480
    assert((a.minWidth.isFinite && b.minWidth.isFinite) || (a.minWidth == double.infinity && b.minWidth == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
    assert((a.maxWidth.isFinite && b.maxWidth.isFinite) || (a.maxWidth == double.infinity && b.maxWidth == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
    assert((a.minHeight.isFinite && b.minHeight.isFinite) || (a.minHeight == double.infinity && b.minHeight == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
    assert((a.maxHeight.isFinite && b.maxHeight.isFinite) || (a.maxHeight == double.infinity && b.maxHeight == double.infinity), 'Cannot interpolate between finite constraints and unbounded constraints.');
481
    return BoxConstraints(
482 483 484 485
      minWidth: a.minWidth.isFinite ? ui.lerpDouble(a.minWidth, b.minWidth, t)! : double.infinity,
      maxWidth: a.maxWidth.isFinite ? ui.lerpDouble(a.maxWidth, b.maxWidth, t)! : double.infinity,
      minHeight: a.minHeight.isFinite ? ui.lerpDouble(a.minHeight, b.minHeight, t)! : double.infinity,
      maxHeight: a.maxHeight.isFinite ? ui.lerpDouble(a.maxHeight, b.maxHeight, t)! : double.infinity,
Adam Barth's avatar
Adam Barth committed
486 487 488
    );
  }

489
  /// Returns whether the object's constraints are normalized.
490
  /// Constraints are normalized if the minimums are less than or
491 492 493 494 495 496 497
  /// equal to the corresponding maximums.
  ///
  /// For example, a BoxConstraints object with a minWidth of 100.0
  /// and a maxWidth of 90.0 is not normalized.
  ///
  /// Most of the APIs on BoxConstraints expect the constraints to be
  /// normalized and have undefined behavior when they are not. In
498
  /// debug mode, many of these APIs will assert if the constraints
499
  /// are not normalized.
500
  @override
501 502 503 504 505 506
  bool get isNormalized {
    return minWidth >= 0.0 &&
           minWidth <= maxWidth &&
           minHeight >= 0.0 &&
           minHeight <= maxHeight;
  }
507

508
  @override
509
  bool debugAssertIsValid({
510
    bool isAppliedConstraint = false,
511
    InformationCollector? informationCollector,
512
  }) {
513
    assert(() {
514
      void throwError(DiagnosticsNode message) {
515 516 517 518 519
        throw FlutterError.fromParts(<DiagnosticsNode>[
          message,
          if (informationCollector != null) ...informationCollector(),
          DiagnosticsProperty<BoxConstraints>('The offending constraints were', this, style: DiagnosticsTreeStyle.errorProperty),
        ]);
520
      }
521
      if (minWidth.isNaN || maxWidth.isNaN || minHeight.isNaN || maxHeight.isNaN) {
522 523 524 525 526 527
        final List<String> affectedFieldsList = <String>[
          if (minWidth.isNaN) 'minWidth',
          if (maxWidth.isNaN) 'maxWidth',
          if (minHeight.isNaN) 'minHeight',
          if (maxHeight.isNaN) 'maxHeight',
        ];
528
        assert(affectedFieldsList.isNotEmpty);
529 530 531 532 533 534 535 536 537 538
        if (affectedFieldsList.length > 1)
          affectedFieldsList.add('and ${affectedFieldsList.removeLast()}');
        String whichFields = '';
        if (affectedFieldsList.length > 2) {
          whichFields = affectedFieldsList.join(', ');
        } else if (affectedFieldsList.length == 2) {
          whichFields = affectedFieldsList.join(' ');
        } else {
          whichFields = affectedFieldsList.single;
        }
539
        throwError(ErrorSummary('BoxConstraints has ${affectedFieldsList.length == 1 ? 'a NaN value' : 'NaN values' } in $whichFields.'));
540
      }
541
      if (minWidth < 0.0 && minHeight < 0.0)
542
        throwError(ErrorSummary('BoxConstraints has both a negative minimum width and a negative minimum height.'));
543
      if (minWidth < 0.0)
544
        throwError(ErrorSummary('BoxConstraints has a negative minimum width.'));
545
      if (minHeight < 0.0)
546
        throwError(ErrorSummary('BoxConstraints has a negative minimum height.'));
547
      if (maxWidth < minWidth && maxHeight < minHeight)
548
        throwError(ErrorSummary('BoxConstraints has both width and height constraints non-normalized.'));
549
      if (maxWidth < minWidth)
550
        throwError(ErrorSummary('BoxConstraints has non-normalized width constraints.'));
551
      if (maxHeight < minHeight)
552
        throwError(ErrorSummary('BoxConstraints has non-normalized height constraints.'));
553 554
      if (isAppliedConstraint) {
        if (minWidth.isInfinite && minHeight.isInfinite)
555
          throwError(ErrorSummary('BoxConstraints forces an infinite width and infinite height.'));
556
        if (minWidth.isInfinite)
557
          throwError(ErrorSummary('BoxConstraints forces an infinite width.'));
558
        if (minHeight.isInfinite)
559
          throwError(ErrorSummary('BoxConstraints forces an infinite height.'));
560 561 562
      }
      assert(isNormalized);
      return true;
563
    }());
564 565 566
    return isNormalized;
  }

567 568 569 570
  /// Returns a box constraints that [isNormalized].
  ///
  /// The returned [maxWidth] is at least as large as the [minWidth]. Similarly,
  /// the returned [maxHeight] is at least as large as the [minHeight].
571
  BoxConstraints normalize() {
572 573 574 575
    if (isNormalized)
      return this;
    final double minWidth = this.minWidth >= 0.0 ? this.minWidth : 0.0;
    final double minHeight = this.minHeight >= 0.0 ? this.minHeight : 0.0;
576
    return BoxConstraints(
577 578 579
      minWidth: minWidth,
      maxWidth: minWidth > maxWidth ? minWidth : maxWidth,
      minHeight: minHeight,
580
      maxHeight: minHeight > maxHeight ? minHeight : maxHeight,
581 582 583
    );
  }

584
  @override
585
  bool operator ==(Object other) {
586
    assert(debugAssertIsValid());
587 588
    if (identical(this, other))
      return true;
589
    if (other.runtimeType != runtimeType)
Hixie's avatar
Hixie committed
590
      return false;
591 592 593 594 595 596
    assert(other is BoxConstraints && other.debugAssertIsValid());
    return other is BoxConstraints
        && other.minWidth == minWidth
        && other.maxWidth == maxWidth
        && other.minHeight == minHeight
        && other.maxHeight == maxHeight;
597
  }
Hixie's avatar
Hixie committed
598

599
  @override
600
  int get hashCode {
601
    assert(debugAssertIsValid());
602
    return hashValues(minWidth, maxWidth, minHeight, maxHeight);
603 604
  }

605
  @override
Hixie's avatar
Hixie committed
606
  String toString() {
607
    final String annotation = isNormalized ? '' : '; NOT NORMALIZED';
608
    if (minWidth == double.infinity && minHeight == double.infinity)
609
      return 'BoxConstraints(biggest$annotation)';
610 611
    if (minWidth == 0 && maxWidth == double.infinity &&
        minHeight == 0 && maxHeight == double.infinity)
612
      return 'BoxConstraints(unconstrained$annotation)';
Hixie's avatar
Hixie committed
613 614 615 616 617 618 619
    String describe(double min, double max, String dim) {
      if (min == max)
        return '$dim=${min.toStringAsFixed(1)}';
      return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}';
    }
    final String width = describe(minWidth, maxWidth, 'w');
    final String height = describe(minHeight, maxHeight, 'h');
620
    return 'BoxConstraints($width, $height$annotation)';
Hixie's avatar
Hixie committed
621
  }
622 623
}

624 625 626 627 628 629 630 631 632 633 634
/// Method signature for hit testing a [RenderBox].
///
/// Used by [BoxHitTestResult.addWithPaintTransform] to hit test children
/// of a [RenderBox].
///
/// See also:
///
///  * [RenderBox.hitTest], which documents more details around hit testing
///    [RenderBox]es.
typedef BoxHitTest = bool Function(BoxHitTestResult result, Offset position);

635 636 637 638 639 640 641 642 643 644 645 646
/// Method signature for hit testing a [RenderBox] with a manually
/// managed position (one that is passed out-of-band).
///
/// Used by [RenderSliverSingleBoxAdapter.hitTestBoxChild] to hit test
/// [RenderBox] children of a [RenderSliver].
///
/// See also:
///
///  * [RenderBox.hitTest], which documents more details around hit testing
///    [RenderBox]es.
typedef BoxHitTestWithOutOfBandPosition = bool Function(BoxHitTestResult result);

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
/// The result of performing a hit test on [RenderBox]es.
///
/// An instance of this class is provided to [RenderBox.hitTest] to record the
/// result of the hit test.
class BoxHitTestResult extends HitTestResult {
  /// Creates an empty hit test result for hit testing on [RenderBox].
  BoxHitTestResult() : super();

  /// Wraps `result` to create a [HitTestResult] that implements the
  /// [BoxHitTestResult] protocol for hit testing on [RenderBox]es.
  ///
  /// This method is used by [RenderObject]s that adapt between the
  /// [RenderBox]-world and the non-[RenderBox]-world to convert a (subtype of)
  /// [HitTestResult] to a [BoxHitTestResult] for hit testing on [RenderBox]es.
  ///
  /// The [HitTestEntry] instances added to the returned [BoxHitTestResult] are
  /// also added to the wrapped `result` (both share the same underlying data
  /// structure to store [HitTestEntry] instances).
  ///
  /// See also:
  ///
  ///  * [HitTestResult.wrap], which turns a [BoxHitTestResult] back into a
  ///    generic [HitTestResult].
  ///  * [SliverHitTestResult.wrap], which turns a [BoxHitTestResult] into a
  ///    [SliverHitTestResult] for hit testing on [RenderSliver] children.
  BoxHitTestResult.wrap(HitTestResult result) : super.wrap(result);

  /// Transforms `position` to the local coordinate system of a child for
  /// hit-testing the child.
  ///
  /// The actual hit testing of the child needs to be implemented in the
  /// provided `hitTest` callback, which is invoked with the transformed
  /// `position` as argument.
  ///
681 682 683 684
  /// The provided paint `transform` (which describes the transform from the
  /// child to the parent in 3D) is processed by
  /// [PointerEvent.removePerspectiveTransform] to remove the
  /// perspective component and inverted before it is used to transform
685 686 687 688 689 690 691 692 693 694 695
  /// `position` from the coordinate system of the parent to the system of the
  /// child.
  ///
  /// If `transform` is null it will be treated as the identity transform and
  /// `position` is provided to the `hitTest` callback as-is. If `transform`
  /// cannot be inverted, the `hitTest` callback is not invoked and false is
  /// returned. Otherwise, the return value of the `hitTest` callback is
  /// returned.
  ///
  /// The `position` argument may be null, which will be forwarded to the
  /// `hitTest` callback as-is. Using null as the position can be useful if
696
  /// the child speaks a different hit test protocol than the parent and the
697 698
  /// position is not required to do the actual hit testing in that protocol.
  ///
699 700
  /// The function returns the return value of the `hitTest` callback.
  ///
701
  /// {@tool snippet}
702 703
  /// This method is used in [RenderBox.hitTestChildren] when the child and
  /// parent don't share the same origin.
704 705
  ///
  /// ```dart
706
  /// abstract class RenderFoo extends RenderBox {
707 708 709 710 711 712 713 714
  ///   final Matrix4 _effectiveTransform = Matrix4.rotationZ(50);
  ///
  ///   @override
  ///   void applyPaintTransform(RenderBox child, Matrix4 transform) {
  ///     transform.multiply(_effectiveTransform);
  ///   }
  ///
  ///   @override
715
  ///   bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
  ///     return result.addWithPaintTransform(
  ///       transform: _effectiveTransform,
  ///       position: position,
  ///       hitTest: (BoxHitTestResult result, Offset position) {
  ///         return super.hitTestChildren(result, position: position);
  ///       },
  ///     );
  ///   }
  /// }
  /// ```
  /// {@end-tool}
  ///
  /// See also:
  ///
  ///  * [addWithPaintOffset], which can be used for `transform`s that are just
  ///    simple matrix translations by an [Offset].
  ///  * [addWithRawTransform], which takes a transform matrix that is directly
  ///    used to transform the position without any pre-processing.
  bool addWithPaintTransform({
735 736 737
    required Matrix4? transform,
    required Offset position,
    required BoxHitTest hitTest,
738
  }) {
739
    assert(position != null);
740 741
    assert(hitTest != null);
    if (transform != null) {
742
      transform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(transform));
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
      if (transform == null) {
        // Objects are not visible on screen and cannot be hit-tested.
        return false;
      }
    }
    return addWithRawTransform(
      transform: transform,
      position: position,
      hitTest: hitTest,
    );
  }

  /// Convenience method for hit testing children, that are translated by
  /// an [Offset].
  ///
  /// The actual hit testing of the child needs to be implemented in the
  /// provided `hitTest` callback, which is invoked with the transformed
  /// `position` as argument.
  ///
  /// This method can be used as a convenience over [addWithPaintTransform] if
  /// a parent paints a child at an `offset`.
  ///
  /// A null value for `offset` is treated as if [Offset.zero] was provided.
  ///
767 768
  /// The function returns the return value of the `hitTest` callback.
  ///
769
  /// See also:
770 771 772 773
  ///
  ///  * [addWithPaintTransform], which takes a generic paint transform matrix and
  ///    documents the intended usage of this API in more detail.
  bool addWithPaintOffset({
774 775 776
    required Offset? offset,
    required Offset position,
    required BoxHitTest hitTest,
777
  }) {
778
    assert(position != null);
779
    assert(hitTest != null);
780
    final Offset transformedPosition = offset == null ? position : position - offset;
781 782 783 784 785 786 787 788
    if (offset != null) {
      pushOffset(-offset);
    }
    final bool isHit = hitTest(this, transformedPosition);
    if (offset != null) {
      popTransform();
    }
    return isHit;
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
  }

  /// Transforms `position` to the local coordinate system of a child for
  /// hit-testing the child.
  ///
  /// The actual hit testing of the child needs to be implemented in the
  /// provided `hitTest` callback, which is invoked with the transformed
  /// `position` as argument.
  ///
  /// Unlike [addWithPaintTransform], the provided `transform` matrix is used
  /// directly to transform `position` without any pre-processing.
  ///
  /// If `transform` is null it will be treated as the identity transform ad
  /// `position` is provided to the `hitTest` callback as-is.
  ///
  /// The function returns the return value of the `hitTest` callback.
  ///
806
  /// See also:
807 808 809 810
  ///
  ///  * [addWithPaintTransform], which accomplishes the same thing, but takes a
  ///    _paint_ transform matrix.
  bool addWithRawTransform({
811 812 813
    required Matrix4? transform,
    required Offset position,
    required BoxHitTest hitTest,
814
  }) {
815
    assert(position != null);
816
    assert(hitTest != null);
817 818 819
    assert(position != null);
    final Offset transformedPosition = transform == null ?
        position : MatrixUtils.transformPoint(transform, position);
820 821 822 823 824 825 826 827
    if (transform != null) {
      pushTransform(transform);
    }
    final bool isHit = hitTest(this, transformedPosition);
    if (transform != null) {
      popTransform();
    }
    return isHit;
828
  }
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864

  /// Pass-through method for adding a hit test while manually managing
  /// the position transformation logic.
  ///
  /// The actual hit testing of the child needs to be implemented in the
  /// provided `hitTest` callback. The position needs to be handled by
  /// the caller.
  ///
  /// The function returns the return value of the `hitTest` callback.
  ///
  /// A `paintOffset`, `paintTransform`, or `rawTransform` should be
  /// passed to the method to update the hit test stack.
  ///
  ///  * `paintOffset` has the semantics of the `offset` passed to
  ///    [addWithPaintOffset].
  ///
  ///  * `paintTransform` has the semantics of the `transform` passed to
  ///    [addWithPaintTransform], except that it must be invertible; it
  ///    is the responsibility of the caller to ensure this.
  ///
  ///  * `rawTransform` has the semantics of the `transform` passed to
  ///    [addWithRawTransform].
  ///
  /// Exactly one of these must be non-null.
  ///
  /// See also:
  ///
  ///  * [addWithPaintTransform], which takes a generic paint transform matrix and
  ///    documents the intended usage of this API in more detail.
  bool addWithOutOfBandPosition({
    Offset? paintOffset,
    Matrix4? paintTransform,
    Matrix4? rawTransform,
    required BoxHitTestWithOutOfBandPosition hitTest,
  }) {
    assert(hitTest != null);
865 866 867 868 869 870
    assert(
      (paintOffset == null && paintTransform == null && rawTransform != null) ||
      (paintOffset == null && paintTransform != null && rawTransform == null) ||
      (paintOffset != null && paintTransform == null && rawTransform == null),
      'Exactly one transform or offset argument must be provided.',
    );
871 872 873 874 875 876 877 878 879 880 881 882 883 884
    if (paintOffset != null) {
      pushOffset(-paintOffset);
    } else if (rawTransform != null) {
      pushTransform(rawTransform);
    } else {
      assert(paintTransform != null);
      paintTransform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(paintTransform!));
      assert(paintTransform != null, 'paintTransform must be invertible.');
      pushTransform(paintTransform!);
    }
    final bool isHit = hitTest(this);
    popTransform();
    return isHit;
  }
885 886
}

887
/// A hit test entry used by [RenderBox].
888
class BoxHitTestEntry extends HitTestEntry {
889 890 891
  /// Creates a box hit test entry.
  ///
  /// The [localPosition] argument must not be null.
892
  BoxHitTestEntry(RenderBox target, this.localPosition)
893 894
    : assert(localPosition != null),
      super(target);
Ian Hickson's avatar
Ian Hickson committed
895

896
  @override
897
  RenderBox get target => super.target as RenderBox;
Adam Barth's avatar
Adam Barth committed
898

899
  /// The position of the hit test in the local coordinates of [target].
900
  final Offset localPosition;
Ian Hickson's avatar
Ian Hickson committed
901

902
  @override
903
  String toString() => '${describeIdentity(target)}@$localPosition';
904 905
}

906
/// Parent data used by [RenderBox] and its subclasses.
907
class BoxParentData extends ParentData {
908
  /// The offset at which to paint the child in the parent's coordinate system.
909
  Offset offset = Offset.zero;
910

911
  @override
912
  String toString() => 'offset=$offset';
913 914
}

915 916
/// Abstract [ParentData] subclass for [RenderBox] subclasses that want the
/// [ContainerRenderObjectMixin].
917 918 919 920
///
/// This is a convenience class that mixes in the relevant classes with
/// the relevant type arguments.
abstract class ContainerBoxParentData<ChildType extends RenderObject> extends BoxParentData with ContainerParentDataMixin<ChildType> { }
Hixie's avatar
Hixie committed
921

922
enum _IntrinsicDimension { minWidth, maxWidth, minHeight, maxHeight }
923 924

@immutable
925
class _IntrinsicDimensionsCacheEntry {
926
  const _IntrinsicDimensionsCacheEntry(this.dimension, this.argument);
927 928 929 930 931

  final _IntrinsicDimension dimension;
  final double argument;

  @override
932
  bool operator ==(Object other) {
933 934 935
    return other is _IntrinsicDimensionsCacheEntry
        && other.dimension == dimension
        && other.argument == argument;
936 937 938 939 940 941
  }

  @override
  int get hashCode => hashValues(dimension, argument);
}

942
/// A render object in a 2D Cartesian coordinate system.
Adam Barth's avatar
Adam Barth committed
943
///
944 945 946 947 948
/// The [size] of each box is expressed as a width and a height. Each box has
/// its own coordinate system in which its upper left corner is placed at (0,
/// 0). The lower right corner of the box is therefore at (width, height). The
/// box contains all the points including the upper left corner and extending
/// to, but not including, the lower right corner.
Adam Barth's avatar
Adam Barth committed
949 950
///
/// Box layout is performed by passing a [BoxConstraints] object down the tree.
951 952
/// The box constraints establish a min and max value for the child's width and
/// height. In determining its size, the child must respect the constraints
Adam Barth's avatar
Adam Barth committed
953 954 955
/// given to it by its parent.
///
/// This protocol is sufficient for expressing a number of common box layout
956
/// data flows. For example, to implement a width-in-height-out data flow, call
Adam Barth's avatar
Adam Barth committed
957 958 959
/// your child's [layout] function with a set of box constraints with a tight
/// width value (and pass true for parentUsesSize). After the child determines
/// its height, use the child's height to determine your size.
960 961 962 963 964
///
/// ## Writing a RenderBox subclass
///
/// One would implement a new [RenderBox] subclass to describe a new layout
/// model, new paint model, new hit-testing model, or new semantics model, while
965
/// remaining in the Cartesian space defined by the [RenderBox] protocol.
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
///
/// To create a new protocol, consider subclassing [RenderObject] instead.
///
/// ### Constructors and properties of a new RenderBox subclass
///
/// The constructor will typically take a named argument for each property of
/// the class. The value is then passed to a private field of the class and the
/// constructor asserts its correctness (e.g. if it should not be null, it
/// asserts it's not null).
///
/// Properties have the form of a getter/setter/field group like the following:
///
/// ```dart
/// AxisDirection get axis => _axis;
/// AxisDirection _axis;
/// set axis(AxisDirection value) {
///   assert(value != null); // same check as in the constructor
///   if (value == _axis)
///     return;
///   _axis = value;
///   markNeedsLayout();
/// }
/// ```
///
/// The setter will typically finish with either a call to [markNeedsLayout], if
/// the layout uses this property, or [markNeedsPaint], if only the painter
/// function does. (No need to call both, [markNeedsLayout] implies
/// [markNeedsPaint].)
///
/// Consider layout and paint to be expensive; be conservative about calling
/// [markNeedsLayout] or [markNeedsPaint]. They should only be called if the
/// layout (or paint, respectively) has actually changed.
///
/// ### Children
///
/// If a render object is a leaf, that is, it cannot have any children, then
/// ignore this section. (Examples of leaf render objects are [RenderImage] and
/// [RenderParagraph].)
///
/// For render objects with children, there are four possible scenarios:
///
/// * A single [RenderBox] child. In this scenario, consider inheriting from
///   [RenderProxyBox] (if the render object sizes itself to match the child) or
///   [RenderShiftedBox] (if the child will be smaller than the box and the box
///   will align the child inside itself).
///
/// * A single child, but it isn't a [RenderBox]. Use the
///   [RenderObjectWithChildMixin] mixin.
///
/// * A single list of children. Use the [ContainerRenderObjectMixin] mixin.
///
/// * A more complicated child model.
///
/// #### Using RenderProxyBox
///
/// By default, a [RenderProxyBox] render object sizes itself to fit its child, or
/// to be as small as possible if there is no child; it passes all hit testing
/// and painting on to the child, and intrinsic dimensions and baseline
/// measurements similarly are proxied to the child.
///
/// A subclass of [RenderProxyBox] just needs to override the parts of the
/// [RenderBox] protocol that matter. For example, [RenderOpacity] just
/// overrides the paint method (and [alwaysNeedsCompositing] to reflect what the
/// paint method does, and the [visitChildrenForSemantics] method so that the
/// child is hidden from accessibility tools when it's invisible), and adds an
/// [RenderOpacity.opacity] field.
///
/// [RenderProxyBox] assumes that the child is the size of the parent and
/// positioned at 0,0. If this is not true, then use [RenderShiftedBox] instead.
///
/// See
/// [proxy_box.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/proxy_box.dart)
/// for examples of inheriting from [RenderProxyBox].
///
/// #### Using RenderShiftedBox
///
/// By default, a [RenderShiftedBox] acts much like a [RenderProxyBox] but
/// without assuming that the child is positioned at 0,0 (the actual position
/// recorded in the child's [parentData] field is used), and without providing a
/// default layout algorithm.
///
/// See
/// [shifted_box.dart](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/shifted_box.dart)
/// for examples of inheriting from [RenderShiftedBox].
///
/// #### Kinds of children and child-specific data
///
/// A [RenderBox] doesn't have to have [RenderBox] children. One can use another
/// subclass of [RenderObject] for a [RenderBox]'s children. See the discussion
/// at [RenderObject].
///
/// Children can have additional data owned by the parent but stored on the
/// child using the [parentData] field. The class used for that data must
/// inherit from [ParentData]. The [setupParentData] method is used to
1060
/// initialize the [parentData] field of a child when the child is attached.
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
///
/// By convention, [RenderBox] objects that have [RenderBox] children use the
/// [BoxParentData] class, which has a [BoxParentData.offset] field to store the
/// position of the child relative to the parent. ([RenderProxyBox] does not
/// need this offset and therefore is an exception to this rule.)
///
/// #### Using RenderObjectWithChildMixin
///
/// If a render object has a single child but it isn't a [RenderBox], then the
/// [RenderObjectWithChildMixin] class, which is a mixin that will handle the
/// boilerplate of managing a child, will be useful.
///
/// It's a generic class with one type argument, the type of the child. For
/// example, if you are building a `RenderFoo` class which takes a single
/// `RenderBar` child, you would use the mixin as follows:
///
/// ```dart
/// class RenderFoo extends RenderBox
///   with RenderObjectWithChildMixin<RenderBar> {
///   // ...
/// }
/// ```
///
/// Since the `RenderFoo` class itself is still a [RenderBox] in this case, you
/// still have to implement the [RenderBox] layout algorithm, as well as
/// features like intrinsics and baselines, painting, and hit testing.
///
/// #### Using ContainerRenderObjectMixin
///
/// If a render box can have multiple children, then the
/// [ContainerRenderObjectMixin] mixin can be used to handle the boilerplate. It
/// uses a linked list to model the children in a manner that is easy to mutate
/// dynamically and that can be walked efficiently. Random access is not
/// efficient in this model; if you need random access to the children consider
/// the next section on more complicated child models.
///
/// The [ContainerRenderObjectMixin] class has two type arguments. The first is
/// the type of the child objects. The second is the type for their
/// [parentData]. The class used for [parentData] must itself have the
/// [ContainerParentDataMixin] class mixed into it; this is where
/// [ContainerRenderObjectMixin] stores the linked list. A [ParentData] class
1102
/// can extend [ContainerBoxParentData]; this is essentially
1103 1104 1105 1106 1107
/// [BoxParentData] mixed with [ContainerParentDataMixin]. For example, if a
/// `RenderFoo` class wanted to have a linked list of [RenderBox] children, one
/// might create a `FooParentData` class as follows:
///
/// ```dart
1108
/// class FooParentData extends ContainerBoxParentData<RenderBox> {
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
///   // (any fields you might need for these children)
/// }
/// ```
///
/// When using [ContainerRenderObjectMixin] in a [RenderBox], consider mixing in
/// [RenderBoxContainerDefaultsMixin], which provides a collection of utility
/// methods that implement common parts of the [RenderBox] protocol (such as
/// painting the children).
///
/// The declaration of the `RenderFoo` class itself would thus look like this:
///
/// ```dart
1121
/// class RenderFoo extends RenderBox with
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
///   ContainerRenderObjectMixin<RenderBox, FooParentData>,
///   RenderBoxContainerDefaultsMixin<RenderBox, FooParentData> {
///   // ...
/// }
/// ```
///
/// When walking the children (e.g. during layout), the following pattern is
/// commonly used (in this case assuming that the children are all [RenderBox]
/// objects and that this render object uses `FooParentData` objects for its
/// children's [parentData] fields):
///
/// ```dart
/// RenderBox child = firstChild;
/// while (child != null) {
///   final FooParentData childParentData = child.parentData;
///   // ...operate on child and childParentData...
///   assert(child.parentData == childParentData);
///   child = childParentData.nextSibling;
/// }
/// ```
///
/// #### More complicated child models
///
/// Render objects can have more complicated models, for example a map of
/// children keyed on an enum, or a 2D grid of efficiently randomly-accessible
/// children, or multiple lists of children, etc. If a render object has a model
/// that can't be handled by the mixins above, it must implement the
/// [RenderObject] child protocol, as follows:
///
/// * Any time a child is removed, call [dropChild] with the child.
///
/// * Any time a child is added, call [adoptChild] with the child.
///
/// * Implement the [attach] method such that it calls [attach] on each child.
///
/// * Implement the [detach] method such that it calls [detach] on each child.
///
/// * Implement the [redepthChildren] method such that it calls [redepthChild]
///   on each child.
///
/// * Implement the [visitChildren] method such that it calls its argument for
///   each child, typically in paint order (back-most to front-most).
///
1165
/// * Implement [debugDescribeChildren] such that it outputs a [DiagnosticsNode]
1166
///   for each child.
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
///
/// Implementing these seven bullet points is essentially all that the two
/// aforementioned mixins do.
///
/// ### Layout
///
/// [RenderBox] classes implement a layout algorithm. They have a set of
/// constraints provided to them, and they size themselves based on those
/// constraints and whatever other inputs they may have (for example, their
/// children or properties).
///
/// When implementing a [RenderBox] subclass, one must make a choice. Does it
/// size itself exclusively based on the constraints, or does it use any other
/// information in sizing itself? An example of sizing purely based on the
/// constraints would be growing to fit the parent.
///
/// Sizing purely based on the constraints allows the system to make some
1184
/// significant optimizations. Classes that use this approach should override
1185 1186
/// [sizedByParent] to return true, and then override [computeDryLayout] to
/// compute the [Size] using nothing but the constraints, e.g.:
1187 1188 1189 1190 1191 1192
///
/// ```dart
/// @override
/// bool get sizedByParent => true;
///
/// @override
1193 1194
/// Size computeDryLayout(BoxConstraints constraints) {
///   return constraints.smallest;
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
/// }
/// ```
///
/// Otherwise, the size is set in the [performLayout] function.
///
/// The [performLayout] function is where render boxes decide, if they are not
/// [sizedByParent], what [size] they should be, and also where they decide
/// where their children should be.
///
/// #### Layout of RenderBox children
///
/// The [performLayout] function should call the [layout] function of each (box)
/// child, passing it a [BoxConstraints] object describing the constraints
/// within which the child can render. Passing tight constraints (see
/// [BoxConstraints.isTight]) to the child will allow the rendering library to
1210
/// apply some optimizations, as it knows that if the constraints are tight, the
1211 1212 1213 1214 1215 1216 1217 1218 1219
/// child's dimensions cannot change even if the layout of the child itself
/// changes.
///
/// If the [performLayout] function will use the child's size to affect other
/// aspects of the layout, for example if the render box sizes itself around the
/// child, or positions several children based on the size of those children,
/// then it must specify the `parentUsesSize` argument to the child's [layout]
/// function, setting it to true.
///
1220
/// This flag turns off some optimizations; algorithms that do not rely on the
1221 1222 1223 1224 1225 1226
/// children's sizes will be more efficient. (In particular, relying on the
/// child's [size] means that if the child is marked dirty for layout, the
/// parent will probably also be marked dirty for layout, unless the
/// [constraints] given by the parent to the child were tight constraints.)
///
/// For [RenderBox] classes that do not inherit from [RenderProxyBox], once they
1227
/// have laid out their children, they should also position them, by setting the
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
/// [BoxParentData.offset] field of each child's [parentData] object.
///
/// #### Layout of non-RenderBox children
///
/// The children of a [RenderBox] do not have to be [RenderBox]es themselves. If
/// they use another protocol (as discussed at [RenderObject]), then instead of
/// [BoxConstraints], the parent would pass in the appropriate [Constraints]
/// subclass, and instead of reading the child's size, the parent would read
/// whatever the output of [layout] is for that layout protocol. The
/// `parentUsesSize` flag is still used to indicate whether the parent is going
1238
/// to read that output, and optimizations still kick in if the child has tight
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
/// constraints (as defined by [Constraints.isTight]).
///
/// ### Painting
///
/// To describe how a render box paints, implement the [paint] method. It is
/// given a [PaintingContext] object and an [Offset]. The painting context
/// provides methods to affect the layer tree as well as a
/// [PaintingContext.canvas] which can be used to add drawing commands. The
/// canvas object should not be cached across calls to the [PaintingContext]'s
/// methods; every time a method on [PaintingContext] is called, there is a
/// chance that the canvas will change identity. The offset specifies the
/// position of the top left corner of the box in the coordinate system of the
/// [PaintingContext.canvas].
///
/// To draw text on a canvas, use a [TextPainter].
///
/// To draw an image to a canvas, use the [paintImage] method.
///
/// A [RenderBox] that uses methods on [PaintingContext] that introduce new
/// layers should override the [alwaysNeedsCompositing] getter and set it to
/// true. If the object sometimes does and sometimes does not, it can have that
/// getter return true in some cases and false in others. In that case, whenever
/// the return value would change, call [markNeedsCompositingBitsUpdate]. (This
/// is done automatically when a child is added or removed, so you don't have to
/// call it explicitly if the [alwaysNeedsCompositing] getter only changes value
/// based on the presence or absence of children.)
///
/// Anytime anything changes on the object that would cause the [paint] method
/// to paint something different (but would not cause the layout to change),
/// the object should call [markNeedsPaint].
///
/// #### Painting children
///
/// The [paint] method's `context` argument has a [PaintingContext.paintChild]
/// method, which should be called for each child that is to be painted. It
/// should be given a reference to the child, and an [Offset] giving the
/// position of the child relative to the parent.
///
/// If the [paint] method applies a transform to the painting context before
/// painting children (or generally applies an additional offset beyond the
/// offset it was itself given as an argument), then the [applyPaintTransform]
/// method should also be overridden. That method must adjust the matrix that it
/// is given in the same manner as it transformed the painting context and
/// offset before painting the given child. This is used by the [globalToLocal]
/// and [localToGlobal] methods.
///
/// #### Hit Tests
///
/// Hit testing for render boxes is implemented by the [hitTest] method. The
/// default implementation of this method defers to [hitTestSelf] and
/// [hitTestChildren]. When implementing hit testing, you can either override
/// these latter two methods, or ignore them and just override [hitTest].
///
1292
/// The [hitTest] method itself is given an [Offset], and must return true if the
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
/// object or one of its children has absorbed the hit (preventing objects below
/// this one from being hit), or false if the hit can continue to other objects
/// below this one.
///
/// For each child [RenderBox], the [hitTest] method on the child should be
/// called with the same [HitTestResult] argument and with the point transformed
/// into the child's coordinate space (in the same manner that the
/// [applyPaintTransform] method would). The default implementation defers to
/// [hitTestChildren] to call the children. [RenderBoxContainerDefaultsMixin]
/// provides a [RenderBoxContainerDefaultsMixin.defaultHitTestChildren] method
/// that does this assuming that the children are axis-aligned, not transformed,
/// and positioned according to the [BoxParentData.offset] field of the
/// [parentData]; more elaborate boxes can override [hitTestChildren]
/// accordingly.
///
/// If the object is hit, then it should also add itself to the [HitTestResult]
/// object that is given as an argument to the [hitTest] method, using
/// [HitTestResult.add]. The default implementation defers to [hitTestSelf] to
/// determine if the box is hit. If the object adds itself before the children
/// can add themselves, then it will be as if the object was above the children.
/// If it adds itself after the children, then it will be as if it was below the
/// children. Entries added to the [HitTestResult] object should use the
/// [BoxHitTestEntry] class. The entries are subsequently walked by the system
/// in the order they were added, and for each entry, the target's [handleEvent]
/// method is called, passing in the [HitTestEntry] object.
///
/// Hit testing cannot rely on painting having happened.
///
/// ### Semantics
///
/// For a render box to be accessible, implement the
1324 1325 1326 1327 1328 1329
/// [describeApproximatePaintClip], [visitChildrenForSemantics], and
/// [describeSemanticsConfiguration] methods. The default implementations are
/// sufficient for objects that only affect layout, but nodes that represent
/// interactive components or information (diagrams, text, images, etc) should
/// provide more complete implementations. For more information, see the
/// documentation for these members.
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
///
/// ### Intrinsics and Baselines
///
/// The layout, painting, hit testing, and semantics protocols are common to all
/// render objects. [RenderBox] objects must implement two additional protocols:
/// intrinsic sizing and baseline measurements.
///
/// There are four methods to implement for intrinsic sizing, to compute the
/// minimum and maximum intrinsic width and height of the box. The documentation
/// for these methods discusses the protocol in detail:
/// [computeMinIntrinsicWidth], [computeMaxIntrinsicWidth],
/// [computeMinIntrinsicHeight], [computeMaxIntrinsicHeight].
///
1343 1344 1345 1346
/// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if you
/// do override any of these methods, which will add additional checks to
/// help validate your implementation.
///
1347 1348 1349 1350 1351 1352
/// In addition, if the box has any children, it must implement
/// [computeDistanceToActualBaseline]. [RenderProxyBox] provides a simple
/// implementation that forwards to the child; [RenderShiftedBox] provides an
/// implementation that offsets the child's baseline information by the position
/// of the child relative to the parent. If you do not inherited from either of
/// these classes, however, you must implement the algorithm yourself.
1353
abstract class RenderBox extends RenderObject {
1354
  @override
1355
  void setupParentData(covariant RenderObject child) {
1356
    if (child.parentData is! BoxParentData)
1357
      child.parentData = BoxParentData();
1358 1359
  }

1360
  Map<_IntrinsicDimensionsCacheEntry, double>? _cachedIntrinsicDimensions;
1361

1362
  double _computeIntrinsicDimension(_IntrinsicDimension dimension, double argument, double Function(double argument) computer) {
1363 1364 1365 1366 1367 1368 1369 1370
    assert(RenderObject.debugCheckingIntrinsics || !debugDoingThisResize); // performResize should not depend on anything except the incoming constraints
    bool shouldCache = true;
    assert(() {
      // we don't want the checked-mode intrinsic tests to affect
      // who gets marked dirty, etc.
      if (RenderObject.debugCheckingIntrinsics)
        shouldCache = false;
      return true;
1371
    }());
1372 1373
    if (shouldCache) {
      _cachedIntrinsicDimensions ??= <_IntrinsicDimensionsCacheEntry, double>{};
1374
      return _cachedIntrinsicDimensions!.putIfAbsent(
1375
        _IntrinsicDimensionsCacheEntry(dimension, argument),
1376
        () => computer(argument),
1377 1378 1379 1380 1381
      );
    }
    return computer(argument);
  }

1382 1383 1384 1385 1386 1387 1388
  /// Returns the minimum width that this box could be without failing to
  /// correctly paint its contents within itself, without clipping.
  ///
  /// The height argument may give a specific height to assume. The given height
  /// can be infinite, meaning that the intrinsic width in an unconstrained
  /// environment is being requested. The given height should never be negative
  /// or null.
Adam Barth's avatar
Adam Barth committed
1389
  ///
1390 1391 1392 1393
  /// This function should only be called on one's children. Calling this
  /// function couples the child with the parent so that when the child's layout
  /// changes, the parent is notified (via [markNeedsLayout]).
  ///
1394
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1395
  ///
1396
  /// Do not override this method. Instead, implement [computeMinIntrinsicWidth].
1397
  @mustCallSuper
1398
  double getMinIntrinsicWidth(double height) {
1399
    assert(() {
1400
      if (height == null) {
1401 1402 1403
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMinIntrinsicWidth was null.'),
          ErrorDescription('The argument to getMinIntrinsicWidth must not be negative or null.'),
1404
          ErrorHint('If you do not have a specific height in mind, then pass double.infinity instead.'),
1405
        ]);
1406 1407
      }
      if (height < 0.0) {
1408 1409 1410 1411 1412 1413
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMinIntrinsicWidth was negative.'),
          ErrorDescription('The argument to getMinIntrinsicWidth must not be negative or null.'),
          ErrorHint(
            'If you perform computations on another height before passing it to '
            'getMinIntrinsicWidth, consider using math.max() or double.clamp() '
1414
            'to force the value into the valid range.',
1415 1416
          ),
        ]);
1417 1418
      }
      return true;
1419
    }());
1420 1421 1422 1423 1424 1425 1426 1427 1428
    return _computeIntrinsicDimension(_IntrinsicDimension.minWidth, height, computeMinIntrinsicWidth);
  }

  /// Computes the value returned by [getMinIntrinsicWidth]. Do not call this
  /// function directly, instead, call [getMinIntrinsicWidth].
  ///
  /// Override in subclasses that implement [performLayout]. This method should
  /// return the minimum width that this box could be without failing to
  /// correctly paint its contents within itself, without clipping.
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
  ///
  /// If the layout algorithm is independent of the context (e.g. it always
  /// tries to be a particular size), or if the layout algorithm is
  /// width-in-height-out, or if the layout algorithm uses both the incoming
  /// width and height constraints (e.g. it always sizes itself to
  /// [BoxConstraints.biggest]), then the `height` argument should be ignored.
  ///
  /// If the layout algorithm is strictly height-in-width-out, or is
  /// height-in-width-out when the width is unconstrained, then the height
  /// argument is the height to use.
  ///
1440 1441
  /// The `height` argument will never be negative or null. It may be infinite.
  ///
1442 1443 1444 1445
  /// If this algorithm depends on the intrinsic dimensions of a child, the
  /// intrinsic dimensions of that child should be obtained using the functions
  /// whose names start with `get`, not `compute`.
  ///
1446 1447
  /// This function should never return a negative or infinite value.
  ///
1448 1449 1450 1451
  /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
  /// you do override this method, which will add additional checks to help
  /// validate your implementation.
  ///
1452 1453 1454 1455 1456 1457 1458 1459
  /// ## Examples
  ///
  /// ### Text
  ///
  /// Text is the canonical example of a width-in-height-out algorithm. The
  /// `height` argument is therefore ignored.
  ///
  /// Consider the string "Hello World" The _maximum_ intrinsic width (as
1460
  /// returned from [computeMaxIntrinsicWidth]) would be the width of the string
1461 1462 1463 1464 1465 1466
  /// with no line breaks.
  ///
  /// The minimum intrinsic width would be the width of the widest word, "Hello"
  /// or "World". If the text is rendered in an even narrower width, however, it
  /// might still not overflow. For example, maybe the rendering would put a
  /// line-break half-way through the words, as in "Hel⁞lo⁞Wor⁞ld". However,
1467
  /// this wouldn't be a _correct_ rendering, and [computeMinIntrinsicWidth] is
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
  /// supposed to render the minimum width that the box could be without failing
  /// to _correctly_ paint the contents within itself.
  ///
  /// The minimum intrinsic _height_ for a given width smaller than the minimum
  /// intrinsic width could therefore be greater than the minimum intrinsic
  /// height for the minimum intrinsic width.
  ///
  /// ### Viewports (e.g. scrolling lists)
  ///
  /// Some render boxes are intended to clip their children. For example, the
  /// render box for a scrolling list might always size itself to its parents'
  /// size (or rather, to the maximum incoming constraints), regardless of the
  /// children's sizes, and then clip the children and position them based on
  /// the current scroll offset.
  ///
  /// The intrinsic dimensions in these cases still depend on the children, even
  /// though the layout algorithm sizes the box in a way independent of the
  /// children. It is the size that is needed to paint the box's contents (in
  /// this case, the children) _without clipping_ that matters.
  ///
1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
  /// ### When the intrinsic dimensions cannot be known
  ///
  /// There are cases where render objects do not have an efficient way to
  /// compute their intrinsic dimensions. For example, it may be prohibitively
  /// expensive to reify and measure every child of a lazy viewport (viewports
  /// generally only instantiate the actually visible children), or the
  /// dimensions may be computed by a callback about which the render object
  /// cannot reason.
  ///
  /// In such cases, it may be impossible (or at least impractical) to actually
  /// return a valid answer. In such cases, the intrinsic functions should throw
  /// when [RenderObject.debugCheckingIntrinsics] is false and asserts are
  /// enabled, and return 0.0 otherwise.
  ///
  /// See the implementations of [LayoutBuilder] or [RenderViewportBase] for
  /// examples (in particular,
  /// [RenderViewportBase.debugThrowIfNotCheckingIntrinsics]).
1505 1506 1507 1508 1509 1510 1511 1512
  ///
  /// ### Aspect-ratio-driven boxes
  ///
  /// Some boxes always return a fixed size based on the constraints. For these
  /// boxes, the intrinsic functions should return the appropriate size when the
  /// incoming `height` or `width` argument is finite, treating that as a tight
  /// constraint in the respective direction and treating the other direction's
  /// constraints as unbounded. This is because the definitions of
1513 1514 1515
  /// [computeMinIntrinsicWidth] and [computeMinIntrinsicHeight] are in terms of
  /// what the dimensions _could be_, and such boxes can only be one size in
  /// such cases.
1516 1517 1518
  ///
  /// When the incoming argument is not finite, then they should return the
  /// actual intrinsic dimensions based on the contents, as any other box would.
1519 1520 1521 1522 1523
  ///
  /// See also:
  ///
  ///  * [computeMaxIntrinsicWidth], which computes the smallest width beyond
  ///    which increasing the width never decreases the preferred height.
1524
  @protected
1525
  double computeMinIntrinsicWidth(double height) {
1526
    return 0.0;
1527 1528
  }

Adam Barth's avatar
Adam Barth committed
1529
  /// Returns the smallest width beyond which increasing the width never
1530 1531
  /// decreases the preferred height. The preferred height is the value that
  /// would be returned by [getMinIntrinsicHeight] for that width.
Adam Barth's avatar
Adam Barth committed
1532
  ///
1533 1534 1535 1536 1537
  /// The height argument may give a specific height to assume. The given height
  /// can be infinite, meaning that the intrinsic width in an unconstrained
  /// environment is being requested. The given height should never be negative
  /// or null.
  ///
1538 1539 1540 1541
  /// This function should only be called on one's children. Calling this
  /// function couples the child with the parent so that when the child's layout
  /// changes, the parent is notified (via [markNeedsLayout]).
  ///
1542
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1543
  ///
1544
  /// Do not override this method. Instead, implement
1545 1546
  /// [computeMaxIntrinsicWidth].
  @mustCallSuper
1547
  double getMaxIntrinsicWidth(double height) {
1548
    assert(() {
1549
      if (height == null) {
1550 1551 1552
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMaxIntrinsicWidth was null.'),
          ErrorDescription('The argument to getMaxIntrinsicWidth must not be negative or null.'),
1553
          ErrorHint('If you do not have a specific height in mind, then pass double.infinity instead.'),
1554
        ]);
1555 1556
      }
      if (height < 0.0) {
1557 1558 1559 1560 1561 1562
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMaxIntrinsicWidth was negative.'),
          ErrorDescription('The argument to getMaxIntrinsicWidth must not be negative or null.'),
          ErrorHint(
            'If you perform computations on another height before passing it to '
            'getMaxIntrinsicWidth, consider using math.max() or double.clamp() '
1563
            'to force the value into the valid range.',
1564
          ),
1565
        ]);
1566 1567
      }
      return true;
1568
    }());
1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
    return _computeIntrinsicDimension(_IntrinsicDimension.maxWidth, height, computeMaxIntrinsicWidth);
  }

  /// Computes the value returned by [getMaxIntrinsicWidth]. Do not call this
  /// function directly, instead, call [getMaxIntrinsicWidth].
  ///
  /// Override in subclasses that implement [performLayout]. This should return
  /// the smallest width beyond which increasing the width never decreases the
  /// preferred height. The preferred height is the value that would be returned
  /// by [computeMinIntrinsicHeight] for that width.
1579 1580 1581
  ///
  /// If the layout algorithm is strictly height-in-width-out, or is
  /// height-in-width-out when the width is unconstrained, then this should
1582
  /// return the same value as [computeMinIntrinsicWidth] for the same height.
1583 1584 1585
  ///
  /// Otherwise, the height argument should be ignored, and the returned value
  /// should be equal to or bigger than the value returned by
1586
  /// [computeMinIntrinsicWidth].
1587
  ///
1588 1589
  /// The `height` argument will never be negative or null. It may be infinite.
  ///
1590 1591 1592 1593 1594
  /// The value returned by this method might not match the size that the object
  /// would actually take. For example, a [RenderBox] subclass that always
  /// exactly sizes itself using [BoxConstraints.biggest] might well size itself
  /// bigger than its max intrinsic size.
  ///
1595 1596 1597 1598
  /// If this algorithm depends on the intrinsic dimensions of a child, the
  /// intrinsic dimensions of that child should be obtained using the functions
  /// whose names start with `get`, not `compute`.
  ///
1599 1600
  /// This function should never return a negative or infinite value.
  ///
1601 1602 1603 1604
  /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
  /// you do override this method, which will add additional checks to help
  /// validate your implementation.
  ///
1605 1606 1607
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1608
  @protected
1609
  double computeMaxIntrinsicWidth(double height) {
1610
    return 0.0;
1611 1612
  }

1613 1614 1615 1616 1617 1618 1619
  /// Returns the minimum height that this box could be without failing to
  /// correctly paint its contents within itself, without clipping.
  ///
  /// The width argument may give a specific width to assume. The given width
  /// can be infinite, meaning that the intrinsic height in an unconstrained
  /// environment is being requested. The given width should never be negative
  /// or null.
Adam Barth's avatar
Adam Barth committed
1620
  ///
1621 1622 1623 1624
  /// This function should only be called on one's children. Calling this
  /// function couples the child with the parent so that when the child's layout
  /// changes, the parent is notified (via [markNeedsLayout]).
  ///
1625
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1626
  ///
1627
  /// Do not override this method. Instead, implement
1628 1629
  /// [computeMinIntrinsicHeight].
  @mustCallSuper
1630
  double getMinIntrinsicHeight(double width) {
1631
    assert(() {
1632
      if (width == null) {
1633 1634 1635
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMinIntrinsicHeight was null.'),
          ErrorDescription('The argument to getMinIntrinsicHeight must not be negative or null.'),
1636
          ErrorHint('If you do not have a specific width in mind, then pass double.infinity instead.'),
1637
        ]);
1638 1639
      }
      if (width < 0.0) {
1640 1641 1642 1643 1644 1645
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMinIntrinsicHeight was negative.'),
          ErrorDescription('The argument to getMinIntrinsicHeight must not be negative or null.'),
          ErrorHint(
            'If you perform computations on another width before passing it to '
            'getMinIntrinsicHeight, consider using math.max() or double.clamp() '
1646
            'to force the value into the valid range.',
1647
          ),
1648
        ]);
1649 1650
      }
      return true;
1651
    }());
1652 1653 1654 1655 1656 1657 1658 1659 1660
    return _computeIntrinsicDimension(_IntrinsicDimension.minHeight, width, computeMinIntrinsicHeight);
  }

  /// Computes the value returned by [getMinIntrinsicHeight]. Do not call this
  /// function directly, instead, call [getMinIntrinsicHeight].
  ///
  /// Override in subclasses that implement [performLayout]. Should return the
  /// minimum height that this box could be without failing to correctly paint
  /// its contents within itself, without clipping.
1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671
  ///
  /// If the layout algorithm is independent of the context (e.g. it always
  /// tries to be a particular size), or if the layout algorithm is
  /// height-in-width-out, or if the layout algorithm uses both the incoming
  /// height and width constraints (e.g. it always sizes itself to
  /// [BoxConstraints.biggest]), then the `width` argument should be ignored.
  ///
  /// If the layout algorithm is strictly width-in-height-out, or is
  /// width-in-height-out when the height is unconstrained, then the width
  /// argument is the width to use.
  ///
1672 1673
  /// The `width` argument will never be negative or null. It may be infinite.
  ///
1674 1675 1676 1677
  /// If this algorithm depends on the intrinsic dimensions of a child, the
  /// intrinsic dimensions of that child should be obtained using the functions
  /// whose names start with `get`, not `compute`.
  ///
1678 1679
  /// This function should never return a negative or infinite value.
  ///
1680 1681 1682 1683
  /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
  /// you do override this method, which will add additional checks to help
  /// validate your implementation.
  ///
1684 1685 1686
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1687 1688
  ///  * [computeMaxIntrinsicHeight], which computes the smallest height beyond
  ///    which increasing the height never decreases the preferred width.
1689
  @protected
1690
  double computeMinIntrinsicHeight(double width) {
1691
    return 0.0;
1692 1693
  }

Adam Barth's avatar
Adam Barth committed
1694
  /// Returns the smallest height beyond which increasing the height never
1695 1696
  /// decreases the preferred width. The preferred width is the value that
  /// would be returned by [getMinIntrinsicWidth] for that height.
Adam Barth's avatar
Adam Barth committed
1697
  ///
1698 1699 1700 1701 1702
  /// The width argument may give a specific width to assume. The given width
  /// can be infinite, meaning that the intrinsic height in an unconstrained
  /// environment is being requested. The given width should never be negative
  /// or null.
  ///
1703 1704 1705 1706
  /// This function should only be called on one's children. Calling this
  /// function couples the child with the parent so that when the child's layout
  /// changes, the parent is notified (via [markNeedsLayout]).
  ///
1707
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1708
  ///
1709
  /// Do not override this method. Instead, implement
1710 1711
  /// [computeMaxIntrinsicHeight].
  @mustCallSuper
1712
  double getMaxIntrinsicHeight(double width) {
1713
    assert(() {
1714
      if (width == null) {
1715 1716 1717
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMaxIntrinsicHeight was null.'),
          ErrorDescription('The argument to getMaxIntrinsicHeight must not be negative or null.'),
1718
          ErrorHint('If you do not have a specific width in mind, then pass double.infinity instead.'),
1719
        ]);
1720 1721
      }
      if (width < 0.0) {
1722 1723 1724 1725 1726 1727
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMaxIntrinsicHeight was negative.'),
          ErrorDescription('The argument to getMaxIntrinsicHeight must not be negative or null.'),
          ErrorHint(
            'If you perform computations on another width before passing it to '
            'getMaxIntrinsicHeight, consider using math.max() or double.clamp() '
1728
            'to force the value into the valid range.',
1729
          ),
1730
        ]);
1731 1732
      }
      return true;
1733
    }());
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
    return _computeIntrinsicDimension(_IntrinsicDimension.maxHeight, width, computeMaxIntrinsicHeight);
  }

  /// Computes the value returned by [getMaxIntrinsicHeight]. Do not call this
  /// function directly, instead, call [getMaxIntrinsicHeight].
  ///
  /// Override in subclasses that implement [performLayout]. Should return the
  /// smallest height beyond which increasing the height never decreases the
  /// preferred width. The preferred width is the value that would be returned
  /// by [computeMinIntrinsicWidth] for that height.
1744 1745 1746
  ///
  /// If the layout algorithm is strictly width-in-height-out, or is
  /// width-in-height-out when the height is unconstrained, then this should
1747
  /// return the same value as [computeMinIntrinsicHeight] for the same width.
1748 1749 1750
  ///
  /// Otherwise, the width argument should be ignored, and the returned value
  /// should be equal to or bigger than the value returned by
1751
  /// [computeMinIntrinsicHeight].
1752
  ///
1753 1754
  /// The `width` argument will never be negative or null. It may be infinite.
  ///
1755 1756 1757 1758 1759
  /// The value returned by this method might not match the size that the object
  /// would actually take. For example, a [RenderBox] subclass that always
  /// exactly sizes itself using [BoxConstraints.biggest] might well size itself
  /// bigger than its max intrinsic size.
  ///
1760 1761 1762 1763
  /// If this algorithm depends on the intrinsic dimensions of a child, the
  /// intrinsic dimensions of that child should be obtained using the functions
  /// whose names start with `get`, not `compute`.
  ///
1764 1765
  /// This function should never return a negative or infinite value.
  ///
1766 1767 1768 1769
  /// Be sure to set [debugCheckIntrinsicSizes] to true in your unit tests if
  /// you do override this method, which will add additional checks to help
  /// validate your implementation.
  ///
1770 1771 1772
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1773
  @protected
1774
  double computeMaxIntrinsicHeight(double width) {
1775
    return 0.0;
1776 1777
  }

1778
  Map<BoxConstraints, Size>? _cachedDryLayoutSizes;
1779
  bool _computingThisDryLayout = false;
1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810

  /// Returns the [Size] that this [RenderBox] would like to be given the
  /// provided [BoxConstraints].
  ///
  /// The size returned by this method is guaranteed to be the same size that
  /// this [RenderBox] computes for itself during layout given the same
  /// constraints.
  ///
  /// This function should only be called on one's children. Calling this
  /// function couples the child with the parent so that when the child's layout
  /// changes, the parent is notified (via [markNeedsLayout]).
  ///
  /// This layout is called "dry" layout as opposed to the regular "wet" layout
  /// run performed by [performLayout] because it computes the desired size for
  /// the given constraints without changing any internal state.
  ///
  /// Calling this function is expensive as it can result in O(N^2) behavior.
  ///
  /// Do not override this method. Instead, implement [computeDryLayout].
  @mustCallSuper
  Size getDryLayout(BoxConstraints constraints) {
    bool shouldCache = true;
    assert(() {
      // we don't want the checked-mode intrinsic tests to affect
      // who gets marked dirty, etc.
      if (RenderObject.debugCheckingIntrinsics)
        shouldCache = false;
      return true;
    }());
    if (shouldCache) {
      _cachedDryLayoutSizes ??= <BoxConstraints, Size>{};
1811
      return _cachedDryLayoutSizes!.putIfAbsent(constraints, () => _computeDryLayout(constraints));
1812
    }
1813 1814 1815 1816 1817 1818 1819 1820 1821
    return _computeDryLayout(constraints);
  }

  Size _computeDryLayout(BoxConstraints constraints) {
    assert(() {
      assert(!_computingThisDryLayout);
      _computingThisDryLayout = true;
      return true;
    }());
1822
    final Size result = computeDryLayout(constraints);
1823 1824 1825 1826 1827 1828
    assert(() {
      assert(_computingThisDryLayout);
      _computingThisDryLayout = false;
      return true;
    }());
    return result;
1829 1830 1831 1832 1833
  }

  /// Computes the value returned by [getDryLayout]. Do not call this
  /// function directly, instead, call [getDryLayout].
  ///
1834 1835 1836 1837
  /// Override in subclasses that implement [performLayout] or [performResize]
  /// or when setting [sizedByParent] to true without overriding
  /// [performResize]. This method should return the [Size] that this
  /// [RenderBox] would like to be given the provided [BoxConstraints].
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860
  ///
  /// The size returned by this method must match the [size] that the
  /// [RenderBox] will compute for itself in [performLayout] (or
  /// [performResize], if [sizedByParent] is true).
  ///
  /// If this algorithm depends on the size of a child, the size of that child
  /// should be obtained using its [getDryLayout] method.
  ///
  /// This layout is called "dry" layout as opposed to the regular "wet" layout
  /// run performed by [performLayout] because it computes the desired size for
  /// the given constraints without changing any internal state.
  ///
  /// ### When the size cannot be known
  ///
  /// There are cases where render objects do not have an efficient way to
  /// compute their size without doing a full layout. For example, the size
  /// may depend on the baseline of a child (which is not available without
  /// doing a full layout), it may be computed by a callback about which the
  /// render object cannot reason, or the layout is so complex that it
  /// is simply impractical to calculate the size in an efficient way.
  ///
  /// In such cases, it may be impossible (or at least impractical) to actually
  /// return a valid answer. In such cases, the function should call
nt4f04uNd's avatar
nt4f04uNd committed
1861
  /// [debugCannotComputeDryLayout] from within an assert and return a dummy
1862 1863 1864 1865 1866 1867 1868 1869
  /// value of `const Size(0, 0)`.
  @protected
  Size computeDryLayout(BoxConstraints constraints) {
    assert(debugCannotComputeDryLayout(
      error: FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('The ${objectRuntimeType(this, 'RenderBox')} class does not implement "computeDryLayout".'),
        ErrorHint(
          'If you are not writing your own RenderBox subclass, then this is not\n'
1870
          'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.md',
1871 1872 1873
        ),
      ]),
    ));
1874
    return Size.zero;
1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912
  }

  static bool _dryLayoutCalculationValid = true;

  /// Called from [computeDryLayout] within an assert if the given [RenderBox]
  /// subclass does not support calculating a dry layout.
  ///
  /// When asserts are enabled and [debugCheckingIntrinsics] is not true, this
  /// method will either throw the provided [FlutterError] or it will create and
  /// throw a [FlutterError] with the provided `reason`. Otherwise, it will
  /// simply return true.
  ///
  /// One of the arguments has to be provided.
  ///
  /// See also:
  ///
  ///  * [computeDryLayout], which lists some reasons why it may not be feasible
  ///    to compute the dry layout.
  bool debugCannotComputeDryLayout({String? reason, FlutterError? error}) {
    assert((reason == null) != (error == null));
    assert(() {
      if (!RenderObject.debugCheckingIntrinsics) {
        if (reason != null) {
          assert(error ==null);
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('The ${objectRuntimeType(this, 'RenderBox')} class does not support dry layout.'),
            if (reason.isNotEmpty) ErrorDescription(reason),
          ]);
        }
        assert(error != null);
        throw error!;
      }
      _dryLayoutCalculationValid = false;
      return true;
    }());
    return true;
  }

1913 1914 1915
  /// Whether this render object has undergone layout and has a [size].
  bool get hasSize => _size != null;

1916
  /// The size of this render box computed during layout.
1917 1918 1919 1920 1921 1922 1923
  ///
  /// This value is stale whenever this object is marked as needing layout.
  /// During [performLayout], do not read the size of a child unless you pass
  /// true for parentUsesSize when calling the child's [layout] function.
  ///
  /// The size of a box should be set only during the box's [performLayout] or
  /// [performResize] functions. If you wish to change the size of a box outside
1924
  /// of those functions, call [markNeedsLayout] instead to schedule a layout of
1925
  /// the box.
1926
  Size get size {
1927
    assert(hasSize, 'RenderBox was not laid out: ${toString()}');
1928
    assert(() {
1929
      final Size? _size = this._size;
1930 1931
      if (_size is _DebugSize) {
        assert(_size._owner == this);
1932 1933
        if (RenderObject.debugActiveLayout != null &&
            !RenderObject.debugActiveLayout!.debugDoingThisLayoutWithCallback) {
1934
          assert(
1935
            debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||
1936 1937 1938 1939 1940 1941
              (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent),
            'RenderBox.size accessed beyond the scope of resize, layout, or '
            'permitted parent access. RenderBox can always access its own size, '
            'otherwise, the only object that is allowed to read RenderBox.size '
            'is its parent, if they have said they will. It you hit this assert '
            'trying to access a child\'s size, pass "parentUsesSize: true" to '
1942
            "that child's layout().",
1943
          );
1944
        }
Hixie's avatar
Hixie committed
1945
        assert(_size == this._size);
1946 1947
      }
      return true;
1948
    }());
1949
    return _size!;
1950
  }
1951
  Size? _size;
1952
  /// Setting the size, in debug mode, triggers some analysis of the render box,
1953 1954
  /// as implemented by [debugAssertDoesMeetConstraints], including calling the intrinsic
  /// sizing methods and checking that they meet certain invariants.
1955
  @protected
1956
  set size(Size value) {
1957 1958 1959 1960 1961 1962 1963
    assert(!(debugDoingThisResize && debugDoingThisLayout));
    assert(sizedByParent || !debugDoingThisResize);
    assert(() {
      if ((sizedByParent && debugDoingThisResize) ||
          (!sizedByParent && debugDoingThisLayout))
        return true;
      assert(!debugDoingThisResize);
1964 1965 1966
      final List<DiagnosticsNode> information = <DiagnosticsNode>[
        ErrorSummary('RenderBox size setter called incorrectly.'),
      ];
1967 1968
      if (debugDoingThisLayout) {
        assert(sizedByParent);
1969
        information.add(ErrorDescription('It appears that the size setter was called from performLayout().'));
1970
      } else {
1971
        information.add(ErrorDescription(
1972
          'The size setter was called from outside layout (neither performResize() nor performLayout() were being run for this object).',
1973
        ));
1974
        if (owner != null && owner!.debugDoingLayout)
1975
          information.add(ErrorDescription('Only the object itself can set its size. It is a contract violation for other objects to set it.'));
1976 1977
      }
      if (sizedByParent)
1978
        information.add(ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().'));
1979
      else
1980 1981
        information.add(ErrorDescription('Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().'));
      throw FlutterError.fromParts(information);
1982
    }());
1983
    assert(() {
1984
      value = debugAdoptSize(value);
1985
      return true;
1986
    }());
1987
    _size = value;
1988 1989 1990 1991
    assert(() {
      debugAssertDoesMeetConstraints();
      return true;
    }());
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008
  }

  /// Claims ownership of the given [Size].
  ///
  /// In debug mode, the [RenderBox] class verifies that [Size] objects obtained
  /// from other [RenderBox] objects are only used according to the semantics of
  /// the [RenderBox] protocol, namely that a [Size] from a [RenderBox] can only
  /// be used by its parent, and then only if `parentUsesSize` was set.
  ///
  /// Sometimes, a [Size] that can validly be used ends up no longer being valid
  /// over time. The common example is a [Size] taken from a child that is later
  /// removed from the parent. In such cases, this method can be called to first
  /// check whether the size can legitimately be used, and if so, to then create
  /// a new [Size] that can be used going forward, regardless of what happens to
  /// the original owner.
  Size debugAdoptSize(Size value) {
    Size result = value;
2009
    assert(() {
2010 2011 2012
      if (value is _DebugSize) {
        if (value._owner != this) {
          if (value._owner.parent != this) {
2013 2014 2015 2016 2017 2018 2019 2020
            throw FlutterError.fromParts(<DiagnosticsNode>[
              ErrorSummary('The size property was assigned a size inappropriately.'),
              describeForError('The following render object'),
              value._owner.describeForError('...was assigned a size obtained from'),
              ErrorDescription(
                'However, this second render object is not, or is no longer, a '
                'child of the first, and it is therefore a violation of the '
                'RenderBox layout protocol to use that size in the layout of the '
2021
                'first render object.',
2022 2023 2024 2025 2026
              ),
              ErrorHint(
                'If the size was obtained at a time where it was valid to read '
                'the size (because the second render object above was a child '
                'of the first at the time), then it should be adopted using '
2027
                'debugAdoptSize at that time.',
2028 2029 2030 2031 2032 2033
              ),
              ErrorHint(
                'If the size comes from a grandchild or a render object from an '
                'entirely different part of the render tree, then there is no '
                'way to be notified when the size changes and therefore attempts '
                'to read that size are almost certainly a source of bugs. A different '
2034
                'approach should be used.',
2035 2036
              ),
            ]);
2037 2038
          }
          if (!value._canBeUsedByParent) {
2039
            throw FlutterError.fromParts(<DiagnosticsNode>[
2040
              ErrorSummary("A child's size was used without setting parentUsesSize."),
2041 2042 2043 2044 2045 2046 2047 2048
              describeForError('The following render object'),
              value._owner.describeForError('...was assigned a size obtained from its child'),
              ErrorDescription(
                'However, when the child was laid out, the parentUsesSize argument '
                'was not set or set to false. Subsequently this transpired to be '
                'inaccurate: the size was nonetheless used by the parent.\n'
                'It is important to tell the framework if the size will be used or not '
                'as several important performance optimizations can be made if the '
2049
                'size will not be used by the parent.',
2050
              ),
2051
            ]);
2052 2053 2054
          }
        }
      }
2055
      result = _DebugSize(value, this, debugCanParentUseSize);
2056
      return true;
2057
    }());
2058
    return result;
2059 2060
  }

2061
  @override
2062
  Rect get semanticBounds => Offset.zero & size;
Hixie's avatar
Hixie committed
2063

2064
  @override
2065 2066 2067 2068 2069
  void debugResetSize() {
    // updates the value of size._canBeUsedByParent if necessary
    size = size;
  }

2070
  Map<TextBaseline, double?>? _cachedBaselines;
2071 2072 2073 2074 2075
  static bool _debugDoingBaseline = false;
  static bool _debugSetDoingBaseline(bool value) {
    _debugDoingBaseline = value;
    return true;
  }
Adam Barth's avatar
Adam Barth committed
2076 2077 2078 2079 2080 2081 2082 2083

  /// Returns the distance from the y-coordinate of the position of the box to
  /// the y-coordinate of the first given baseline in the box's contents.
  ///
  /// Used by certain layout models to align adjacent boxes on a common
  /// baseline, regardless of padding, font size differences, etc. If there is
  /// no baseline, this function returns the distance from the y-coordinate of
  /// the position of the box to the y-coordinate of the bottom of the box
2084
  /// (i.e., the height of the box) unless the caller passes true
Adam Barth's avatar
Adam Barth committed
2085 2086
  /// for `onlyReal`, in which case the function returns null.
  ///
2087 2088 2089
  /// Only call this function after calling [layout] on this box. You
  /// are only allowed to call this from the parent of this box during
  /// that parent's [performLayout] or [paint] functions.
2090 2091 2092
  ///
  /// When implementing a [RenderBox] subclass, to override the baseline
  /// computation, override [computeDistanceToActualBaseline].
2093
  double? getDistanceToBaseline(TextBaseline baseline, { bool onlyReal = false }) {
2094
    assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2095
    assert(!debugNeedsLayout);
2096
    assert(() {
2097 2098 2099 2100 2101
      final RenderObject? parent = this.parent as RenderObject?;
      if (owner!.debugDoingLayout)
        return (RenderObject.debugActiveLayout == parent) && parent!.debugDoingThisLayout;
      if (owner!.debugDoingPaint)
        return ((RenderObject.debugActivePaint == parent) && parent!.debugDoingThisPaint) ||
2102
               ((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
2103
      assert(parent == this.parent);
2104
      return false;
2105
    }());
2106
    assert(_debugSetDoingBaseline(true));
2107
    final double? result = getDistanceToActualBaseline(baseline);
2108 2109 2110 2111 2112
    assert(_debugSetDoingBaseline(false));
    if (result == null && !onlyReal)
      return size.height;
    return result;
  }
Adam Barth's avatar
Adam Barth committed
2113 2114 2115 2116 2117 2118

  /// Calls [computeDistanceToActualBaseline] and caches the result.
  ///
  /// This function must only be called from [getDistanceToBaseline] and
  /// [computeDistanceToActualBaseline]. Do not call this function directly from
  /// outside those two methods.
2119
  @protected
2120
  @mustCallSuper
2121
  double? getDistanceToActualBaseline(TextBaseline baseline) {
2122
    assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2123 2124 2125
    _cachedBaselines ??= <TextBaseline, double?>{};
    _cachedBaselines!.putIfAbsent(baseline, () => computeDistanceToActualBaseline(baseline));
    return _cachedBaselines![baseline];
2126
  }
Adam Barth's avatar
Adam Barth committed
2127 2128 2129 2130 2131

  /// Returns the distance from the y-coordinate of the position of the box to
  /// the y-coordinate of the first given baseline in the box's contents, if
  /// any, or null otherwise.
  ///
2132 2133 2134
  /// Do not call this function directly. If you need to know the baseline of a
  /// child from an invocation of [performLayout] or [paint], call
  /// [getDistanceToBaseline].
Adam Barth's avatar
Adam Barth committed
2135
  ///
2136
  /// Subclasses should override this method to supply the distances to their
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151
  /// baselines. When implementing this method, there are generally three
  /// strategies:
  ///
  ///  * For classes that use the [ContainerRenderObjectMixin] child model,
  ///    consider mixing in the [RenderBoxContainerDefaultsMixin] class and
  ///    using
  ///    [RenderBoxContainerDefaultsMixin.defaultComputeDistanceToFirstActualBaseline].
  ///
  ///  * For classes that define a particular baseline themselves, return that
  ///    value directly.
  ///
  ///  * For classes that have a child to which they wish to defer the
  ///    computation, call [getDistanceToActualBaseline] on the child (not
  ///    [computeDistanceToActualBaseline], the internal implementation, and not
  ///    [getDistanceToBaseline], the public entry point for this API).
2152
  @protected
2153
  double? computeDistanceToActualBaseline(TextBaseline baseline) {
2154
    assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
2155 2156 2157
    return null;
  }

2158
  /// The box constraints most recently received from the parent.
2159
  @override
2160
  BoxConstraints get constraints => super.constraints as BoxConstraints;
2161 2162

  @override
2163
  void debugAssertDoesMeetConstraints() {
2164
    assert(constraints != null);
2165 2166
    assert(() {
      if (!hasSize) {
2167
        final DiagnosticsNode contract;
2168
        if (sizedByParent)
2169
          contract = ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().');
2170
        else
2171 2172 2173 2174 2175
          contract = ErrorDescription('Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().');
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('RenderBox did not set its size during layout.'),
          contract,
          ErrorDescription('It appears that this did not happen; layout completed, but the size property is still null.'),
2176
          DiagnosticsProperty<RenderBox>('The RenderBox in question is', this, style: DiagnosticsTreeStyle.errorProperty),
2177
        ]);
2178
      }
2179
      // verify that the size is not infinite
2180
      if (!_size!.isFinite) {
2181 2182 2183 2184 2185
        final List<DiagnosticsNode> information = <DiagnosticsNode>[
          ErrorSummary('$runtimeType object was given an infinite size during layout.'),
          ErrorDescription(
            'This probably means that it is a render object that tries to be '
            'as big as possible, but it was put inside another render object '
2186
            'that allows its children to pick their own size.',
2187
          ),
2188
        ];
2189 2190 2191
        if (!constraints.hasBoundedWidth) {
          RenderBox node = this;
          while (!node.constraints.hasBoundedWidth && node.parent is RenderBox)
2192
            node = node.parent! as RenderBox;
2193 2194

          information.add(node.describeForError('The nearest ancestor providing an unbounded width constraint is'));
2195
        }
2196 2197 2198
        if (!constraints.hasBoundedHeight) {
          RenderBox node = this;
          while (!node.constraints.hasBoundedHeight && node.parent is RenderBox)
2199
            node = node.parent! as RenderBox;
2200

2201
          information.add(node.describeForError('The nearest ancestor providing an unbounded height constraint is'));
2202
        }
2203 2204 2205 2206 2207 2208
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ...information,
          DiagnosticsProperty<BoxConstraints>('The constraints that applied to the $runtimeType were', constraints, style: DiagnosticsTreeStyle.errorProperty),
          DiagnosticsProperty<Size>('The exact size it was given was', _size, style: DiagnosticsTreeStyle.errorProperty),
          ErrorHint('See https://flutter.dev/docs/development/ui/layout/box-constraints for more information.'),
        ]);
2209
      }
2210
      // verify that the size is within the constraints
2211
      if (!constraints.isSatisfiedBy(_size!)) {
2212 2213 2214 2215 2216 2217
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('$runtimeType does not meet its constraints.'),
          DiagnosticsProperty<BoxConstraints>('Constraints', constraints, style: DiagnosticsTreeStyle.errorProperty),
          DiagnosticsProperty<Size>('Size', _size, style: DiagnosticsTreeStyle.errorProperty),
          ErrorHint(
            'If you are not writing your own RenderBox subclass, then this is not '
2218
            'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.md',
2219 2220
          ),
        ]);
2221
      }
2222
      if (debugCheckIntrinsicSizes) {
2223
        // verify that the intrinsics are sane
2224 2225
        assert(!RenderObject.debugCheckingIntrinsics);
        RenderObject.debugCheckingIntrinsics = true;
2226
        final List<DiagnosticsNode> failures = <DiagnosticsNode>[];
2227

2228
        double testIntrinsic(double Function(double extent) function, String name, double constraint) {
2229 2230
          final double result = function(constraint);
          if (result < 0) {
2231
            failures.add(ErrorDescription(' * $name($constraint) returned a negative value: $result'));
2232 2233
          }
          if (!result.isFinite) {
2234
            failures.add(ErrorDescription(' * $name($constraint) returned a non-finite value: $result'));
2235 2236
          }
          return result;
2237
        }
2238

2239
        void testIntrinsicsForValues(double Function(double extent) getMin, double Function(double extent) getMax, String name, double constraint) {
2240 2241 2242
          final double min = testIntrinsic(getMin, 'getMinIntrinsic$name', constraint);
          final double max = testIntrinsic(getMax, 'getMaxIntrinsic$name', constraint);
          if (min > max) {
2243
            failures.add(ErrorDescription(' * getMinIntrinsic$name($constraint) returned a larger value ($min) than getMaxIntrinsic$name($constraint) ($max)'));
2244
          }
2245
        }
2246

2247 2248
        testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', double.infinity);
        testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', double.infinity);
2249
        if (constraints.hasBoundedWidth)
2250
          testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', constraints.maxHeight);
2251
        if (constraints.hasBoundedHeight)
2252
          testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', constraints.maxWidth);
2253

2254 2255
        // TODO(ianh): Test that values are internally consistent in more ways than the above.

2256 2257
        RenderObject.debugCheckingIntrinsics = false;
        if (failures.isNotEmpty) {
2258 2259 2260
          // TODO(jacobr): consider nesting the failures object so it is collapsible.
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('The intrinsic dimension methods of the $runtimeType class returned values that violate the intrinsic protocol contract.'),
2261 2262 2263
            ErrorDescription('The following ${failures.length > 1 ? "failures" : "failure"} was detected:'), // should this be tagged as an error or not?
            ...failures,
            ErrorHint(
2264
              'If you are not writing your own RenderBox subclass, then this is not\n'
2265
              'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.md',
2266 2267
            ),
          ]);
2268
        }
2269 2270 2271 2272

        // Checking that getDryLayout computes the same size.
        _dryLayoutCalculationValid = true;
        RenderObject.debugCheckingIntrinsics = true;
nt4f04uNd's avatar
nt4f04uNd committed
2273
        final Size dryLayoutSize;
2274 2275 2276 2277 2278 2279 2280 2281 2282 2283
        try {
          dryLayoutSize = getDryLayout(constraints);
        } finally {
          RenderObject.debugCheckingIntrinsics = false;
        }
        if (_dryLayoutCalculationValid && dryLayoutSize != size) {
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('The size given to the ${objectRuntimeType(this, 'RenderBox')} class differs from the size computed by computeDryLayout.'),
            ErrorDescription(
              'The size computed in ${sizedByParent ? 'performResize' : 'performLayout'} '
2284
              'is $size, which is different from $dryLayoutSize, which was computed by computeDryLayout.',
2285 2286 2287 2288 2289 2290
            ),
            ErrorDescription(
              'The constraints used were $constraints.',
            ),
            ErrorHint(
              'If you are not writing your own RenderBox subclass, then this is not\n'
2291
              'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=2_bug.md',
2292 2293 2294
            ),
          ]);
        }
2295 2296
      }
      return true;
2297
    }());
2298 2299
  }

2300
  @override
2301
  void markNeedsLayout() {
2302
    if ((_cachedBaselines != null && _cachedBaselines!.isNotEmpty) ||
2303 2304
        (_cachedIntrinsicDimensions != null && _cachedIntrinsicDimensions!.isNotEmpty) ||
        (_cachedDryLayoutSizes != null && _cachedDryLayoutSizes!.isNotEmpty)) {
2305 2306 2307 2308 2309 2310 2311
      // If we have cached data, then someone must have used our data.
      // Since the parent will shortly be marked dirty, we can forget that they
      // used the baseline and/or intrinsic dimensions. If they use them again,
      // then we'll fill the cache again, and if we get dirty again, we'll
      // notify them again.
      _cachedBaselines?.clear();
      _cachedIntrinsicDimensions?.clear();
2312
      _cachedDryLayoutSizes?.clear();
2313
      if (parent is RenderObject) {
2314 2315 2316
        markParentNeedsLayout();
        return;
      }
2317 2318 2319
    }
    super.markNeedsLayout();
  }
2320

2321 2322
  /// {@macro flutter.rendering.RenderObject.performResize}
  ///
nt4f04uNd's avatar
nt4f04uNd committed
2323 2324 2325
  /// By default this method sets [size] to the result of [computeDryLayout]
  /// called with the current [constraints]. Instead of overriding this method,
  /// consider overriding [computeDryLayout].
2326
  @override
2327
  void performResize() {
2328
    // default behavior for subclasses that have sizedByParent = true
2329
    size = computeDryLayout(constraints);
2330
    assert(size.isFinite);
2331
  }
2332 2333

  @override
2334
  void performLayout() {
2335 2336
    assert(() {
      if (!sizedByParent) {
2337 2338 2339 2340 2341
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('$runtimeType did not implement performLayout().'),
          ErrorHint(
            'RenderBox subclasses need to either override performLayout() to '
            'set a size and lay out any children, or, set sizedByParent to true '
2342
            'so that performResize() sizes the render object.',
2343
          ),
2344
        ]);
2345 2346
      }
      return true;
2347
    }());
2348 2349
  }

2350
  /// Determines the set of render objects located at the given position.
Adam Barth's avatar
Adam Barth committed
2351
  ///
2352 2353 2354 2355
  /// Returns true, and adds any render objects that contain the point to the
  /// given hit test result, if this render object or one of its descendants
  /// absorbs the hit (preventing objects below this one from being hit).
  /// Returns false if the hit can continue to other objects below this one.
Adam Barth's avatar
Adam Barth committed
2356
  ///
2357 2358 2359 2360
  /// The caller is responsible for transforming [position] from global
  /// coordinates to its location relative to the origin of this [RenderBox].
  /// This [RenderBox] is responsible for checking whether the given position is
  /// within its bounds.
2361
  ///
2362 2363 2364 2365 2366
  /// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
  /// [BoxHitTestResult.addWithPaintOffset], or
  /// [BoxHitTestResult.addWithRawTransform] need to be invoked by the caller
  /// to record the required transform operations in the [HitTestResult]. These
  /// methods will also help with applying the transform to `position`.
2367
  ///
2368 2369 2370 2371 2372 2373
  /// Hit testing requires layout to be up-to-date but does not require painting
  /// to be up-to-date. That means a render object can rely upon [performLayout]
  /// having been called in [hitTest] but cannot rely upon [paint] having been
  /// called. For example, a render object might be a child of a [RenderOpacity]
  /// object, which calls [hitTest] on its children when its opacity is zero
  /// even through it does not [paint] its children.
2374
  bool hitTest(BoxHitTestResult result, { required Offset position }) {
2375 2376
    assert(() {
      if (!hasSize) {
2377
        if (debugNeedsLayout) {
2378 2379 2380 2381
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('Cannot hit test a render box that has never been laid out.'),
            describeForError('The hitTest() method was called on this RenderBox'),
            ErrorDescription(
2382
              "Unfortunately, this object's geometry is not known at this time, "
2383
              'probably because it has never been laid out. '
2384
              'This means it cannot be accurately hit-tested.',
2385 2386 2387 2388
            ),
            ErrorHint(
              'If you are trying '
              'to perform a hit test during the layout phase itself, make sure '
2389
              "you only hit test nodes that have completed layout (e.g. the node's "
2390
              'children, after their layout() method has been called).',
2391
            ),
2392
          ]);
2393
        }
2394 2395 2396 2397 2398
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('Cannot hit test a render box with no size.'),
          describeForError('The hitTest() method was called on this RenderBox'),
          ErrorDescription(
            'Although this node is not marked as needing layout, '
2399
            'its size is not set.',
2400 2401 2402 2403
          ),
          ErrorHint(
            'A RenderBox object must have an '
            'explicit size before it can be hit-tested. Make sure '
2404
            'that the RenderBox in question sets its size during layout.',
2405 2406
          ),
        ]);
2407 2408
      }
      return true;
2409
    }());
2410
    if (_size!.contains(position)) {
Adam Barth's avatar
Adam Barth committed
2411
      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
2412
        result.add(BoxHitTestEntry(this, position));
Adam Barth's avatar
Adam Barth committed
2413 2414
        return true;
      }
2415 2416
    }
    return false;
2417
  }
Adam Barth's avatar
Adam Barth committed
2418

2419
  /// Override this method if this render object can be hit even if its
2420
  /// children were not hit.
2421
  ///
2422 2423 2424
  /// Returns true if the specified `position` should be considered a hit
  /// on this render object.
  ///
2425 2426 2427 2428 2429
  /// The caller is responsible for transforming [position] from global
  /// coordinates to its location relative to the origin of this [RenderBox].
  /// This [RenderBox] is responsible for checking whether the given position is
  /// within its bounds.
  ///
2430 2431
  /// Used by [hitTest]. If you override [hitTest] and do not call this
  /// function, then you don't need to implement this function.
2432
  @protected
2433
  bool hitTestSelf(Offset position) => false;
Adam Barth's avatar
Adam Barth committed
2434

2435
  /// Override this method to check whether any children are located at the
2436
  /// given position.
Adam Barth's avatar
Adam Barth committed
2437
  ///
2438 2439 2440
  /// Subclasses should return true if at least one child reported a hit at the
  /// specified position.
  ///
2441
  /// Typically children should be hit-tested in reverse paint order so that
Adam Barth's avatar
Adam Barth committed
2442 2443
  /// hit tests at locations where children overlap hit the child that is
  /// visually "on top" (i.e., paints later).
2444
  ///
2445 2446
  /// The caller is responsible for transforming [position] from global
  /// coordinates to its location relative to the origin of this [RenderBox].
2447 2448
  /// Likewise, this [RenderBox] is responsible for transforming the position
  /// that it passes to its children when it calls [hitTest] on each child.
2449
  ///
2450 2451 2452 2453 2454
  /// If transforming is necessary, [BoxHitTestResult.addWithPaintTransform],
  /// [BoxHitTestResult.addWithPaintOffset], or
  /// [BoxHitTestResult.addWithRawTransform] need to be invoked by subclasses to
  /// record the required transform operations in the [BoxHitTestResult]. These
  /// methods will also help with applying the transform to `position`.
2455
  ///
2456 2457
  /// Used by [hitTest]. If you override [hitTest] and do not call this
  /// function, then you don't need to implement this function.
2458
  @protected
2459
  bool hitTestChildren(BoxHitTestResult result, { required Offset position }) => false;
2460

Adam Barth's avatar
Adam Barth committed
2461
  /// Multiply the transform from the parent's coordinate system to this box's
2462
  /// coordinate system into the given transform.
Adam Barth's avatar
Adam Barth committed
2463 2464 2465 2466
  ///
  /// This function is used to convert coordinate systems between boxes.
  /// Subclasses that apply transforms during painting should override this
  /// function to factor those transforms into the calculation.
2467
  ///
2468
  /// The [RenderBox] implementation takes care of adjusting the matrix for the
2469 2470
  /// position of the given child as determined during layout and stored on the
  /// child's [parentData] in the [BoxParentData.offset] field.
2471
  @override
2472
  void applyPaintTransform(RenderObject child, Matrix4 transform) {
2473
    assert(child != null);
2474
    assert(child.parent == this);
2475 2476
    assert(() {
      if (child.parentData is! BoxParentData) {
2477 2478 2479 2480 2481 2482 2483 2484 2485 2486
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('$runtimeType does not implement applyPaintTransform.'),
          describeForError('The following $runtimeType object'),
          child.describeForError('...did not use a BoxParentData class for the parentData field of the following child'),
          ErrorDescription('The $runtimeType class inherits from RenderBox.'),
          ErrorHint(
            'The default applyPaintTransform implementation provided by RenderBox assumes that the '
            'children all use BoxParentData objects for their parentData field. '
            'Since $runtimeType does not in fact use that ParentData class for its children, it must '
            'provide an implementation of applyPaintTransform that supports the specific ParentData '
2487
            'subclass used by its children (which apparently is ${child.parentData.runtimeType}).',
2488
          ),
2489
        ]);
2490 2491
      }
      return true;
2492
    }());
2493
    final BoxParentData childParentData = child.parentData! as BoxParentData;
2494
    final Offset offset = childParentData.offset;
2495
    transform.translate(offset.dx, offset.dy);
2496 2497
  }

2498
  /// Convert the given point from the global coordinate system in logical pixels
2499
  /// to the local coordinate system for this box.
2500
  ///
2501 2502 2503
  /// This method will un-project the point from the screen onto the widget,
  /// which makes it different from [MatrixUtils.transformPoint].
  ///
2504
  /// If the transform from global coordinates to local coordinates is
2505
  /// degenerate, this function returns [Offset.zero].
2506 2507 2508 2509
  ///
  /// If `ancestor` is non-null, this function converts the given point from the
  /// coordinate system of `ancestor` (which must be an ancestor of this render
  /// object) instead of from the global coordinate system.
2510 2511
  ///
  /// This method is implemented in terms of [getTransformTo].
2512
  Offset globalToLocal(Offset point, { RenderObject? ancestor }) {
2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524
    // We want to find point (p) that corresponds to a given point on the
    // screen (s), but that also physically resides on the local render plane,
    // so that it is useful for visually accurate gesture processing in the
    // local space. For that, we can't simply transform 2D screen point to
    // the 3D local space since the screen space lacks the depth component |z|,
    // and so there are many 3D points that correspond to the screen point.
    // We must first unproject the screen point onto the render plane to find
    // the true 3D point that corresponds to the screen point.
    // We do orthogonal unprojection after undoing perspective, in local space.
    // The render plane is specified by renderBox offset (o) and Z axis (n).
    // Unprojection is done by finding the intersection of the view vector (d)
    // with the local X-Y plane: (o-s).dot(n) == (p-s).dot(n), (p-s) == |z|*d.
2525
    final Matrix4 transform = getTransformTo(ancestor);
2526
    final double det = transform.invert();
2527
    if (det == 0.0)
2528
      return Offset.zero;
2529 2530 2531 2532 2533 2534
    final Vector3 n = Vector3(0.0, 0.0, 1.0);
    final Vector3 i = transform.perspectiveTransform(Vector3(0.0, 0.0, 0.0));
    final Vector3 d = transform.perspectiveTransform(Vector3(0.0, 0.0, 1.0)) - i;
    final Vector3 s = transform.perspectiveTransform(Vector3(point.dx, point.dy, 0.0));
    final Vector3 p = s - d * (n.dot(s) / n.dot(d));
    return Offset(p.x, p.y);
2535 2536
  }

2537
  /// Convert the given point from the local coordinate system for this box to
2538
  /// the global coordinate system in logical pixels.
2539 2540 2541 2542
  ///
  /// If `ancestor` is non-null, this function converts the given point to the
  /// coordinate system of `ancestor` (which must be an ancestor of this render
  /// object) instead of to the global coordinate system.
2543
  ///
2544 2545 2546
  /// This method is implemented in terms of [getTransformTo]. If the transform
  /// matrix puts the given `point` on the line at infinity (for instance, when
  /// the transform matrix is the zero matrix), this method returns (NaN, NaN).
2547
  Offset localToGlobal(Offset point, { RenderObject? ancestor }) {
2548
    return MatrixUtils.transformPoint(getTransformTo(ancestor), point);
2549 2550
  }

2551
  /// Returns a rectangle that contains all the pixels painted by this box.
Adam Barth's avatar
Adam Barth committed
2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563
  ///
  /// The paint bounds can be larger or smaller than [size], which is the amount
  /// of space this box takes up during layout. For example, if this box casts a
  /// shadow, that shadow might extend beyond the space allocated to this box
  /// during layout.
  ///
  /// The paint bounds are used to size the buffers into which this box paints.
  /// If the box attempts to paints outside its paint bounds, there might not be
  /// enough memory allocated to represent the box's visual appearance, which
  /// can lead to undefined behavior.
  ///
  /// The returned paint bounds are in the local coordinate system of this box.
2564
  @override
2565
  Rect get paintBounds => Offset.zero & size;
Adam Barth's avatar
Adam Barth committed
2566

2567
  /// Override this method to handle pointer events that hit this render object.
2568 2569 2570 2571
  ///
  /// For [RenderBox] objects, the `entry` argument is a [BoxHitTestEntry]. From this
  /// object you can determine the [PointerDownEvent]'s position in local coordinates.
  /// (This is useful because [PointerEvent.position] is in global coordinates.)
2572
  ///
2573 2574
  /// Implementations of this method should call [debugHandleEvent] as follows,
  /// so that they support [debugPaintPointersEnabled]:
2575 2576 2577 2578 2579 2580 2581 2582
  ///
  /// ```dart
  /// @override
  /// void handleEvent(PointerEvent event, HitTestEntry entry) {
  ///   assert(debugHandleEvent(event, entry));
  ///   // ... handle the event ...
  /// }
  /// ```
2583
  @override
2584
  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
2585
    super.handleEvent(event, entry);
2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605
  }

  int _debugActivePointers = 0;

  /// Implements the [debugPaintPointersEnabled] debugging feature.
  ///
  /// [RenderBox] subclasses that implement [handleEvent] should call
  /// [debugHandleEvent] from their [handleEvent] method, as follows:
  ///
  /// ```dart
  /// @override
  /// void handleEvent(PointerEvent event, HitTestEntry entry) {
  ///   assert(debugHandleEvent(event, entry));
  ///   // ... handle the event ...
  /// }
  /// ```
  ///
  /// If you call this for a [PointerDownEvent], make sure you also call it for
  /// the corresponding [PointerUpEvent] or [PointerCancelEvent].
  bool debugHandleEvent(PointerEvent event, HitTestEntry entry) {
2606 2607
    assert(() {
      if (debugPaintPointersEnabled) {
Ian Hickson's avatar
Ian Hickson committed
2608
        if (event is PointerDownEvent) {
2609
          _debugActivePointers += 1;
Ian Hickson's avatar
Ian Hickson committed
2610
        } else if (event is PointerUpEvent || event is PointerCancelEvent) {
2611
          _debugActivePointers -= 1;
Ian Hickson's avatar
Ian Hickson committed
2612
        }
2613 2614 2615
        markNeedsPaint();
      }
      return true;
2616
    }());
2617
    return true;
2618 2619
  }

2620
  @override
2621
  void debugPaint(PaintingContext context, Offset offset) {
2622 2623 2624 2625 2626 2627 2628 2629
    assert(() {
      if (debugPaintSizeEnabled)
        debugPaintSize(context, offset);
      if (debugPaintBaselinesEnabled)
        debugPaintBaselines(context, offset);
      if (debugPaintPointersEnabled)
        debugPaintPointers(context, offset);
      return true;
2630
    }());
2631
  }
2632 2633 2634 2635

  /// In debug mode, paints a border around this render box.
  ///
  /// Called for every [RenderBox] when [debugPaintSizeEnabled] is true.
2636
  @protected
2637
  void debugPaintSize(PaintingContext context, Offset offset) {
2638
    assert(() {
2639
      final Paint paint = Paint()
2640
       ..style = PaintingStyle.stroke
2641
       ..strokeWidth = 1.0
2642
       ..color = const Color(0xFF00FFFF);
2643 2644
      context.canvas.drawRect((offset & size).deflate(0.5), paint);
      return true;
2645
    }());
2646
  }
2647 2648 2649 2650

  /// In debug mode, paints a line for each baseline.
  ///
  /// Called for every [RenderBox] when [debugPaintBaselinesEnabled] is true.
2651
  @protected
2652
  void debugPaintBaselines(PaintingContext context, Offset offset) {
2653
    assert(() {
2654
      final Paint paint = Paint()
2655
       ..style = PaintingStyle.stroke
2656 2657 2658
       ..strokeWidth = 0.25;
      Path path;
      // ideographic baseline
2659
      final double? baselineI = getDistanceToBaseline(TextBaseline.ideographic, onlyReal: true);
2660
      if (baselineI != null) {
2661
        paint.color = const Color(0xFFFFD000);
2662
        path = Path();
2663 2664 2665 2666 2667
        path.moveTo(offset.dx, offset.dy + baselineI);
        path.lineTo(offset.dx + size.width, offset.dy + baselineI);
        context.canvas.drawPath(path, paint);
      }
      // alphabetic baseline
2668
      final double? baselineA = getDistanceToBaseline(TextBaseline.alphabetic, onlyReal: true);
2669
      if (baselineA != null) {
2670
        paint.color = const Color(0xFF00FF00);
2671
        path = Path();
2672 2673 2674 2675 2676
        path.moveTo(offset.dx, offset.dy + baselineA);
        path.lineTo(offset.dx + size.width, offset.dy + baselineA);
        context.canvas.drawPath(path, paint);
      }
      return true;
2677
    }());
2678
  }
2679

2680 2681
  /// In debug mode, paints a rectangle if this render box has counted more
  /// pointer downs than pointer up events.
2682 2683
  ///
  /// Called for every [RenderBox] when [debugPaintPointersEnabled] is true.
2684 2685 2686
  ///
  /// By default, events are not counted. For details on how to ensure that
  /// events are counted for your class, see [debugHandleEvent].
2687
  @protected
2688
  void debugPaintPointers(PaintingContext context, Offset offset) {
2689 2690
    assert(() {
      if (_debugActivePointers > 0) {
2691 2692
        final Paint paint = Paint()
         ..color = Color(0x00BBBB | ((0x04000000 * depth) & 0xFF000000));
2693 2694 2695
        context.canvas.drawRect(offset & size, paint);
      }
      return true;
2696
    }());
2697
  }
2698

2699
  @override
2700 2701
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2702
    properties.add(DiagnosticsProperty<Size>('size', _size, missingIfNull: true));
2703
  }
2704 2705
}

2706 2707
/// A mixin that provides useful default behaviors for boxes with children
/// managed by the [ContainerRenderObjectMixin] mixin.
Adam Barth's avatar
Adam Barth committed
2708 2709 2710 2711
///
/// By convention, this class doesn't override any members of the superclass.
/// Instead, it provides helpful functions that subclasses can call as
/// appropriate.
2712
mixin RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, ParentDataType extends ContainerBoxParentData<ChildType>> implements ContainerRenderObjectMixin<ChildType, ParentDataType> {
2713
  /// Returns the baseline of the first child with a baseline.
Adam Barth's avatar
Adam Barth committed
2714 2715 2716
  ///
  /// Useful when the children are displayed vertically in the same order they
  /// appear in the child list.
2717
  double? defaultComputeDistanceToFirstActualBaseline(TextBaseline baseline) {
2718
    assert(!debugNeedsLayout);
2719
    ChildType? child = firstChild;
2720
    while (child != null) {
2721 2722
      final ParentDataType? childParentData = child.parentData as ParentDataType?;
      final double? result = child.getDistanceToActualBaseline(baseline);
2723
      if (result != null)
2724 2725
        return result + childParentData!.offset.dy;
      child = childParentData!.nextSibling;
2726 2727 2728 2729
    }
    return null;
  }

2730
  /// Returns the minimum baseline value among every child.
Adam Barth's avatar
Adam Barth committed
2731 2732 2733
  ///
  /// Useful when the vertical position of the children isn't determined by the
  /// order in the child list.
2734
  double? defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline) {
2735
    assert(!debugNeedsLayout);
2736 2737
    double? result;
    ChildType? child = firstChild;
2738
    while (child != null) {
2739
      final ParentDataType childParentData = child.parentData! as ParentDataType;
2740
      double? candidate = child.getDistanceToActualBaseline(baseline);
2741
      if (candidate != null) {
2742
        candidate += childParentData.offset.dy;
2743 2744 2745 2746 2747
        if (result != null)
          result = math.min(result, candidate);
        else
          result = candidate;
      }
Hixie's avatar
Hixie committed
2748
      child = childParentData.nextSibling;
2749 2750 2751 2752
    }
    return result;
  }

2753
  /// Performs a hit test on each child by walking the child list backwards.
Adam Barth's avatar
Adam Barth committed
2754 2755 2756
  ///
  /// Stops walking once after the first child reports that it contains the
  /// given point. Returns whether any children contain the given point.
2757 2758 2759 2760 2761
  ///
  /// See also:
  ///
  ///  * [defaultPaint], which paints the children appropriate for this
  ///    hit-testing strategy.
2762 2763
  bool defaultHitTestChildren(BoxHitTestResult result, { required Offset position }) {
    ChildType? child = lastChild;
2764
    while (child != null) {
nt4f04uNd's avatar
nt4f04uNd committed
2765
      // The x, y parameters have the top left of the node's box as the origin.
2766
      final ParentDataType childParentData = child.parentData! as ParentDataType;
2767 2768 2769
      final bool isHit = result.addWithPaintOffset(
        offset: childParentData.offset,
        position: position,
2770
        hitTest: (BoxHitTestResult result, Offset transformed) {
2771
          assert(transformed == position - childParentData.offset);
2772
          return child!.hitTest(result, position: transformed);
2773 2774 2775
        },
      );
      if (isHit)
2776
        return true;
Hixie's avatar
Hixie committed
2777
      child = childParentData.previousSibling;
2778
    }
2779
    return false;
2780 2781
  }

2782
  /// Paints each child by walking the child list forwards.
2783 2784 2785 2786 2787
  ///
  /// See also:
  ///
  ///  * [defaultHitTestChildren], which implements hit-testing of the children
  ///    in a manner appropriate for this painting strategy.
2788
  void defaultPaint(PaintingContext context, Offset offset) {
2789
    ChildType? child = firstChild;
2790
    while (child != null) {
2791
      final ParentDataType childParentData = child.parentData! as ParentDataType;
Adam Barth's avatar
Adam Barth committed
2792
      context.paintChild(child, childParentData.offset + offset);
Hixie's avatar
Hixie committed
2793
      child = childParentData.nextSibling;
2794 2795
    }
  }
Adam Barth's avatar
Adam Barth committed
2796

2797 2798 2799 2800 2801
  /// Returns a list containing the children of this render object.
  ///
  /// This function is useful when you need random-access to the children of
  /// this render object. If you're accessing the children in order, consider
  /// walking the child list directly.
Adam Barth's avatar
Adam Barth committed
2802
  List<ChildType> getChildrenAsList() {
2803
    final List<ChildType> result = <ChildType>[];
2804
    RenderBox? child = firstChild;
Adam Barth's avatar
Adam Barth committed
2805
    while (child != null) {
2806
      final ParentDataType childParentData = child.parentData! as ParentDataType;
2807
      result.add(child as ChildType);
Adam Barth's avatar
Adam Barth committed
2808 2809 2810 2811
      child = childParentData.nextSibling;
    }
    return result;
  }
2812
}