box.dart 107 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 20 21 22
  final RenderBox _owner;
  final bool _canBeUsedByParent;
}

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
  /// Creates box constraints that is respected only by the given size.
96 97 98 99 100 101
  BoxConstraints.tight(Size size)
    : minWidth = size.width,
      maxWidth = size.width,
      minHeight = size.height,
      maxHeight = size.height;

102
  /// Creates box constraints that require the given width or height.
103 104 105 106 107 108
  ///
  /// 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.
109 110
  const BoxConstraints.tightFor({
    double width,
111
    double height,
112 113 114 115
  }) : minWidth = width ?? 0.0,
       maxWidth = width ?? double.infinity,
       minHeight = height ?? 0.0,
       maxHeight = height ?? double.infinity;
116

117 118 119 120 121 122 123
  /// 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.
124
  const BoxConstraints.tightForFinite({
125
    double width = double.infinity,
126
    double height = double.infinity,
127 128 129 130
  }) : 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;
131

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

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

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  /// 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;

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

182
  /// Returns new box constraints that are smaller by the given edge dimensions.
183
  BoxConstraints deflate(EdgeInsets edges) {
184
    assert(edges != null);
185
    assert(debugAssertIsValid());
186 187 188 189
    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);
190
    return BoxConstraints(
191 192 193
      minWidth: deflatedMinWidth,
      maxWidth: math.max(deflatedMinWidth, maxWidth - horizontal),
      minHeight: deflatedMinHeight,
194
      maxHeight: math.max(deflatedMinHeight, maxHeight - vertical),
195 196 197
    );
  }

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

209 210
  /// Returns new box constraints that respect the given constraints while being
  /// as close as possible to the original constraints.
211
  BoxConstraints enforce(BoxConstraints constraints) {
212
    return BoxConstraints(
213 214 215 216
      minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth) as double,
      maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth) as double,
      minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight) as double,
      maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight) as double,
217 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.
  BoxConstraints tighten({ double width, double height }) {
224 225 226 227 228 229
    return BoxConstraints(
      minWidth: width == null ? minWidth : width.clamp(minWidth, maxWidth) as double,
      maxWidth: width == null ? maxWidth : width.clamp(minWidth, maxWidth) as double,
      minHeight: height == null ? minHeight : height.clamp(minHeight, maxHeight) as double,
      maxHeight: height == null ? maxHeight : height.clamp(minHeight, maxHeight) as double,
    );
230 231
  }

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

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

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

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

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

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

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

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

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

    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;
    }

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

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

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

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

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

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

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

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

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
  /// 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;

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

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

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

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

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

460
  /// Linearly interpolate between two BoxConstraints.
Adam Barth's avatar
Adam Barth committed
461
  ///
462 463
  /// If either is null, this function interpolates from a [BoxConstraints]
  /// object whose fields are all set to 0.0.
464
  ///
465
  /// {@macro dart.ui.shadow.lerp}
Adam Barth's avatar
Adam Barth committed
466
  static BoxConstraints lerp(BoxConstraints a, BoxConstraints b, double t) {
467
    assert(t != null);
Adam Barth's avatar
Adam Barth committed
468 469 470 471 472 473
    if (a == null && b == null)
      return null;
    if (a == null)
      return b * t;
    if (b == null)
      return a * (1.0 - t);
474 475
    assert(a.debugAssertIsValid());
    assert(b.debugAssertIsValid());
476 477 478 479
    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.');
480
    return BoxConstraints(
481 482 483 484
      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
485 486 487
    );
  }

488
  /// Returns whether the object's constraints are normalized.
489
  /// Constraints are normalized if the minimums are less than or
490 491 492 493 494 495 496 497 498
  /// 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
  /// checked mode, many of these APIs will assert if the constraints
  /// are not normalized.
499
  @override
500 501 502 503 504 505
  bool get isNormalized {
    return minWidth >= 0.0 &&
           minWidth <= maxWidth &&
           minHeight >= 0.0 &&
           minHeight <= maxHeight;
  }
506

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

566 567 568 569
  /// 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].
570
  BoxConstraints normalize() {
571 572 573 574
    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;
575
    return BoxConstraints(
576 577 578
      minWidth: minWidth,
      maxWidth: minWidth > maxWidth ? minWidth : maxWidth,
      minHeight: minHeight,
579
      maxHeight: minHeight > maxHeight ? minHeight : maxHeight,
580 581 582
    );
  }

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

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

604
  @override
Hixie's avatar
Hixie committed
605
  String toString() {
606
    final String annotation = isNormalized ? '' : '; NOT NORMALIZED';
607
    if (minWidth == double.infinity && minHeight == double.infinity)
608
      return 'BoxConstraints(biggest$annotation)';
609 610
    if (minWidth == 0 && maxWidth == double.infinity &&
        minHeight == 0 && maxHeight == double.infinity)
611
      return 'BoxConstraints(unconstrained$annotation)';
Hixie's avatar
Hixie committed
612 613 614 615 616 617 618
    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');
619
    return 'BoxConstraints($width, $height$annotation)';
Hixie's avatar
Hixie committed
620
  }
621 622
}

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
/// 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);

/// 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.
  ///
668 669 670 671
  /// 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
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
  /// `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
  /// the child speaks a different hit test protocol then the parent and the
  /// position is not required to do the actual hit testing in that protocol.
  ///
  /// {@tool sample}
687 688
  /// This method is used in [RenderBox.hitTestChildren] when the child and
  /// parent don't share the same origin.
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
  ///
  /// ```dart
  /// abstract class Foo extends RenderBox {
  ///
  ///   final Matrix4 _effectiveTransform = Matrix4.rotationZ(50);
  ///
  ///   @override
  ///   void applyPaintTransform(RenderBox child, Matrix4 transform) {
  ///     transform.multiply(_effectiveTransform);
  ///   }
  ///
  ///   @override
  ///   bool hitTestChildren(BoxHitTestResult result, { Offset position }) {
  ///     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({
    @required Matrix4 transform,
    @required Offset position,
    @required BoxHitTest hitTest,
  }) {
    assert(hitTest != null);
    if (transform != null) {
727
      transform = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(transform));
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
      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.
  ///
  /// Se also:
  ///
  ///  * [addWithPaintTransform], which takes a generic paint transform matrix and
  ///    documents the intended usage of this API in more detail.
  bool addWithPaintOffset({
    @required Offset offset,
    @required Offset position,
    @required BoxHitTest hitTest,
  }) {
    assert(hitTest != null);
    return addWithRawTransform(
      transform: offset != null ? Matrix4.translationValues(-offset.dx, -offset.dy, 0.0) : null,
      position: position,
      hitTest: hitTest,
    );
  }

  /// 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.
  ///
  /// 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
  /// the child speaks a different hit test protocol then the parent and the
  /// position is not required to do the actual hit testing in that protocol.
  ///
  /// Se also:
  ///
  ///  * [addWithPaintTransform], which accomplishes the same thing, but takes a
  ///    _paint_ transform matrix.
  bool addWithRawTransform({
    @required Matrix4 transform,
    @required Offset position,
    @required BoxHitTest hitTest,
  }) {
    assert(hitTest != null);
    final Offset transformedPosition = position == null || transform == null
        ? position
        : MatrixUtils.transformPoint(transform, position);
802 803 804 805 806 807 808 809
    if (transform != null) {
      pushTransform(transform);
    }
    final bool isHit = hitTest(this, transformedPosition);
    if (transform != null) {
      popTransform();
    }
    return isHit;
810 811 812
  }
}

813
/// A hit test entry used by [RenderBox].
814
class BoxHitTestEntry extends HitTestEntry {
815 816 817
  /// Creates a box hit test entry.
  ///
  /// The [localPosition] argument must not be null.
818
  BoxHitTestEntry(RenderBox target, this.localPosition)
819 820
    : assert(localPosition != null),
      super(target);
Ian Hickson's avatar
Ian Hickson committed
821

822
  @override
823
  RenderBox get target => super.target as RenderBox;
Adam Barth's avatar
Adam Barth committed
824

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

828
  @override
829
  String toString() => '${describeIdentity(target)}@$localPosition';
830 831
}

832
/// Parent data used by [RenderBox] and its subclasses.
833
class BoxParentData extends ParentData {
834
  /// The offset at which to paint the child in the parent's coordinate system.
835
  Offset offset = Offset.zero;
836

837
  @override
838
  String toString() => 'offset=$offset';
839 840
}

Hixie's avatar
Hixie committed
841 842
/// Abstract ParentData subclass for RenderBox subclasses that want the
/// ContainerRenderObjectMixin.
843 844 845 846
///
/// 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
847

848
enum _IntrinsicDimension { minWidth, maxWidth, minHeight, maxHeight }
849 850

@immutable
851
class _IntrinsicDimensionsCacheEntry {
852
  const _IntrinsicDimensionsCacheEntry(this.dimension, this.argument);
853 854 855 856 857 858

  final _IntrinsicDimension dimension;
  final double argument;

  @override
  bool operator ==(dynamic other) {
859 860 861
    return other is _IntrinsicDimensionsCacheEntry
        && other.dimension == dimension
        && other.argument == argument;
862 863 864 865 866 867
  }

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

868
/// A render object in a 2D Cartesian coordinate system.
Adam Barth's avatar
Adam Barth committed
869
///
870 871 872 873 874
/// 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
875 876
///
/// Box layout is performed by passing a [BoxConstraints] object down the tree.
877 878
/// 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
879 880 881
/// given to it by its parent.
///
/// This protocol is sufficient for expressing a number of common box layout
882
/// data flows. For example, to implement a width-in-height-out data flow, call
Adam Barth's avatar
Adam Barth committed
883 884 885
/// 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.
886 887 888 889 890
///
/// ## 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
891
/// remaining in the Cartesian space defined by the [RenderBox] protocol.
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
///
/// 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
986
/// initialize the [parentData] field of a child when the child is attached.
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
///
/// 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
1028
/// can extend [ContainerBoxParentData]; this is essentially
1029 1030 1031 1032 1033
/// [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
1034
/// class FooParentData extends ContainerBoxParentData<RenderBox> {
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
///   // (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
1047
/// class RenderFoo extends RenderBox with
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 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
///   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).
///
1091
/// * Implement [debugDescribeChildren] such that it outputs a [DiagnosticsNode]
1092
///   for each child.
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
///
/// 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
1110
/// significant optimizations. Classes that use this approach should override
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
/// [sizedByParent] to return true, and then override [performResize] to set the
/// [size] using nothing but the constraints, e.g.:
///
/// ```dart
/// @override
/// bool get sizedByParent => true;
///
/// @override
/// void performResize() {
///   size = constraints.smallest;
/// }
/// ```
///
/// 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
1136
/// apply some optimizations, as it knows that if the constraints are tight, the
1137 1138 1139 1140 1141 1142 1143 1144 1145
/// 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.
///
1146
/// This flag turns off some optimizations; algorithms that do not rely on the
1147 1148 1149 1150 1151 1152
/// 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
1153
/// have laid out their children, they should also position them, by setting the
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
/// [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
1164
/// to read that output, and optimizations still kick in if the child has tight
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
/// 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].
///
1218
/// The [hitTest] method itself is given an [Offset], and must return true if the
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
/// 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
/// [describeApproximatePaintClip] and [visitChildrenForSemantics] methods, and
1251
/// the [semanticsAnnotator] getter. The default implementations are sufficient
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
/// 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.
///
/// ### 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].
///
/// 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.
1275
abstract class RenderBox extends RenderObject {
1276
  @override
1277
  void setupParentData(covariant RenderObject child) {
1278
    if (child.parentData is! BoxParentData)
1279
      child.parentData = BoxParentData();
1280 1281
  }

1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
  Map<_IntrinsicDimensionsCacheEntry, double> _cachedIntrinsicDimensions;

  double _computeIntrinsicDimension(_IntrinsicDimension dimension, double argument, double computer(double argument)) {
    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;
1293
    }());
1294 1295 1296
    if (shouldCache) {
      _cachedIntrinsicDimensions ??= <_IntrinsicDimensionsCacheEntry, double>{};
      return _cachedIntrinsicDimensions.putIfAbsent(
1297
        _IntrinsicDimensionsCacheEntry(dimension, argument),
1298
        () => computer(argument),
1299 1300 1301 1302 1303
      );
    }
    return computer(argument);
  }

1304 1305 1306 1307 1308 1309 1310
  /// 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
1311
  ///
1312 1313 1314 1315
  /// 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]).
  ///
1316
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1317
  ///
1318
  /// Do not override this method. Instead, implement [computeMinIntrinsicWidth].
1319 1320
  @mustCallSuper
  double getMinIntrinsicWidth(double height) {
1321 1322
    assert(() {
      if (height == null) {
1323 1324 1325
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMinIntrinsicWidth was null.'),
          ErrorDescription('The argument to getMinIntrinsicWidth must not be negative or null.'),
1326
          ErrorHint('If you do not have a specific height in mind, then pass double.infinity instead.'),
1327
        ]);
1328 1329
      }
      if (height < 0.0) {
1330 1331 1332 1333 1334 1335 1336 1337 1338
        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() '
            'to force the value into the valid range.'
          ),
        ]);
1339 1340
      }
      return true;
1341
    }());
1342 1343 1344 1345 1346 1347 1348 1349 1350
    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.
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
  ///
  /// 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.
  ///
1362 1363
  /// The `height` argument will never be negative or null. It may be infinite.
  ///
1364 1365 1366 1367
  /// 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`.
  ///
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
  /// This function should never return a negative or infinite value.
  ///
  /// ## 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
1378
  /// returned from [computeMaxIntrinsicWidth]) would be the width of the string
1379 1380 1381 1382 1383 1384
  /// 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,
1385
  /// this wouldn't be a _correct_ rendering, and [computeMinIntrinsicWidth] is
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
  /// 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.
  ///
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422
  /// ### 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]).
1423 1424 1425 1426 1427 1428 1429 1430
  ///
  /// ### 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
1431 1432 1433
  /// [computeMinIntrinsicWidth] and [computeMinIntrinsicHeight] are in terms of
  /// what the dimensions _could be_, and such boxes can only be one size in
  /// such cases.
1434 1435 1436
  ///
  /// When the incoming argument is not finite, then they should return the
  /// actual intrinsic dimensions based on the contents, as any other box would.
1437 1438
  @protected
  double computeMinIntrinsicWidth(double height) {
1439
    return 0.0;
1440 1441
  }

Adam Barth's avatar
Adam Barth committed
1442
  /// Returns the smallest width beyond which increasing the width never
1443 1444
  /// 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
1445
  ///
1446 1447 1448 1449 1450
  /// 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.
  ///
1451 1452 1453 1454
  /// 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]).
  ///
1455
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1456
  ///
1457
  /// Do not override this method. Instead, implement
1458 1459 1460
  /// [computeMaxIntrinsicWidth].
  @mustCallSuper
  double getMaxIntrinsicWidth(double height) {
1461 1462
    assert(() {
      if (height == null) {
1463 1464 1465
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The height argument to getMaxIntrinsicWidth was null.'),
          ErrorDescription('The argument to getMaxIntrinsicWidth must not be negative or null.'),
1466
          ErrorHint('If you do not have a specific height in mind, then pass double.infinity instead.'),
1467
        ]);
1468 1469
      }
      if (height < 0.0) {
1470 1471 1472 1473 1474 1475 1476
        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() '
            'to force the value into the valid range.'
1477
          ),
1478
        ]);
1479 1480
      }
      return true;
1481
    }());
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
    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.
1492 1493 1494
  ///
  /// If the layout algorithm is strictly height-in-width-out, or is
  /// height-in-width-out when the width is unconstrained, then this should
1495
  /// return the same value as [computeMinIntrinsicWidth] for the same height.
1496 1497 1498
  ///
  /// Otherwise, the height argument should be ignored, and the returned value
  /// should be equal to or bigger than the value returned by
1499
  /// [computeMinIntrinsicWidth].
1500
  ///
1501 1502
  /// The `height` argument will never be negative or null. It may be infinite.
  ///
1503 1504 1505 1506 1507
  /// 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.
  ///
1508 1509 1510 1511
  /// 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`.
  ///
1512 1513
  /// This function should never return a negative or infinite value.
  ///
1514 1515 1516
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1517 1518
  @protected
  double computeMaxIntrinsicWidth(double height) {
1519
    return 0.0;
1520 1521
  }

1522 1523 1524 1525 1526 1527 1528
  /// 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
1529
  ///
1530 1531 1532 1533
  /// 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]).
  ///
1534
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1535
  ///
1536
  /// Do not override this method. Instead, implement
1537 1538 1539
  /// [computeMinIntrinsicHeight].
  @mustCallSuper
  double getMinIntrinsicHeight(double width) {
1540 1541
    assert(() {
      if (width == null) {
1542 1543 1544
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMinIntrinsicHeight was null.'),
          ErrorDescription('The argument to getMinIntrinsicHeight must not be negative or null.'),
1545
          ErrorHint('If you do not have a specific width in mind, then pass double.infinity instead.'),
1546
        ]);
1547 1548
      }
      if (width < 0.0) {
1549 1550 1551 1552 1553 1554 1555
        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() '
            'to force the value into the valid range.'
1556
          ),
1557
        ]);
1558 1559
      }
      return true;
1560
    }());
1561 1562 1563 1564 1565 1566 1567 1568 1569
    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.
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580
  ///
  /// 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.
  ///
1581 1582
  /// The `width` argument will never be negative or null. It may be infinite.
  ///
1583 1584 1585 1586
  /// 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`.
  ///
1587 1588
  /// This function should never return a negative or infinite value.
  ///
1589 1590 1591
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1592
  @protected
1593
  double computeMinIntrinsicHeight(double width) {
1594
    return 0.0;
1595 1596
  }

Adam Barth's avatar
Adam Barth committed
1597
  /// Returns the smallest height beyond which increasing the height never
1598 1599
  /// 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
1600
  ///
1601 1602 1603 1604 1605
  /// 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.
  ///
1606 1607 1608 1609
  /// 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]).
  ///
1610
  /// Calling this function is expensive as it can result in O(N^2) behavior.
1611
  ///
1612
  /// Do not override this method. Instead, implement
1613 1614 1615
  /// [computeMaxIntrinsicHeight].
  @mustCallSuper
  double getMaxIntrinsicHeight(double width) {
1616 1617
    assert(() {
      if (width == null) {
1618 1619 1620
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('The width argument to getMaxIntrinsicHeight was null.'),
          ErrorDescription('The argument to getMaxIntrinsicHeight must not be negative or null.'),
1621
          ErrorHint('If you do not have a specific width in mind, then pass double.infinity instead.'),
1622
        ]);
1623 1624
      }
      if (width < 0.0) {
1625 1626 1627 1628 1629 1630 1631
        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() '
            'to force the value into the valid range.'
1632
          ),
1633
        ]);
1634 1635
      }
      return true;
1636
    }());
1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
    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.
1647 1648 1649
  ///
  /// If the layout algorithm is strictly width-in-height-out, or is
  /// width-in-height-out when the height is unconstrained, then this should
1650
  /// return the same value as [computeMinIntrinsicHeight] for the same width.
1651 1652 1653
  ///
  /// Otherwise, the width argument should be ignored, and the returned value
  /// should be equal to or bigger than the value returned by
1654
  /// [computeMinIntrinsicHeight].
1655
  ///
1656 1657
  /// The `width` argument will never be negative or null. It may be infinite.
  ///
1658 1659 1660 1661 1662
  /// 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.
  ///
1663 1664 1665 1666
  /// 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`.
  ///
1667 1668
  /// This function should never return a negative or infinite value.
  ///
1669 1670 1671
  /// See also:
  ///
  ///  * [computeMinIntrinsicWidth], which has usage examples.
1672
  @protected
1673
  double computeMaxIntrinsicHeight(double width) {
1674
    return 0.0;
1675 1676
  }

1677 1678 1679
  /// Whether this render object has undergone layout and has a [size].
  bool get hasSize => _size != null;

1680
  /// The size of this render box computed during layout.
1681 1682 1683 1684 1685 1686 1687
  ///
  /// 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
1688
  /// of those functions, call [markNeedsLayout] instead to schedule a layout of
1689 1690
  /// the box.
  Size get size {
1691
    assert(hasSize, 'RenderBox was not laid out: ${toString()}');
1692
    assert(() {
1693
      final Size _size = this._size;
1694 1695 1696
      if (_size is _DebugSize) {
        assert(_size._owner == this);
        if (RenderObject.debugActiveLayout != null) {
1697 1698 1699 1700 1701
          // We are always allowed to access our own size (for print debugging
          // and asserts if nothing else). Other than us, the only object that's
          // allowed to read our size is our parent, if they've said they will.
          // If you hit this assert trying to access a child's size, pass
          // "parentUsesSize: true" to that child's layout().
1702 1703 1704
          assert(debugDoingThisResize || debugDoingThisLayout ||
                 (RenderObject.debugActiveLayout == parent && _size._canBeUsedByParent));
        }
Hixie's avatar
Hixie committed
1705
        assert(_size == this._size);
1706 1707
      }
      return true;
1708
    }());
1709 1710 1711
    return _size;
  }
  Size _size;
1712 1713 1714
  /// Setting the size, in checked mode, triggers some analysis of the render box,
  /// as implemented by [debugAssertDoesMeetConstraints], including calling the intrinsic
  /// sizing methods and checking that they meet certain invariants.
1715
  @protected
1716
  set size(Size value) {
1717 1718 1719 1720 1721 1722 1723
    assert(!(debugDoingThisResize && debugDoingThisLayout));
    assert(sizedByParent || !debugDoingThisResize);
    assert(() {
      if ((sizedByParent && debugDoingThisResize) ||
          (!sizedByParent && debugDoingThisLayout))
        return true;
      assert(!debugDoingThisResize);
1724 1725 1726
      final List<DiagnosticsNode> information = <DiagnosticsNode>[
        ErrorSummary('RenderBox size setter called incorrectly.'),
      ];
1727 1728
      if (debugDoingThisLayout) {
        assert(sizedByParent);
1729
        information.add(ErrorDescription('It appears that the size setter was called from performLayout().'));
1730
      } else {
1731 1732 1733
        information.add(ErrorDescription(
          'The size setter was called from outside layout (neither performResize() nor performLayout() were being run for this object).'
        ));
1734
        if (owner != null && owner.debugDoingLayout)
1735
          information.add(ErrorDescription('Only the object itself can set its size. It is a contract violation for other objects to set it.'));
1736 1737
      }
      if (sizedByParent)
1738
        information.add(ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().'));
1739
      else
1740 1741
        information.add(ErrorDescription('Because this RenderBox has sizedByParent set to false, it must set its size in performLayout().'));
      throw FlutterError.fromParts(information);
1742
    }());
1743
    assert(() {
1744
      value = debugAdoptSize(value);
1745
      return true;
1746
    }());
1747
    _size = value;
1748 1749 1750 1751
    assert(() {
      debugAssertDoesMeetConstraints();
      return true;
    }());
1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768
  }

  /// 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;
1769
    assert(() {
1770 1771 1772
      if (value is _DebugSize) {
        if (value._owner != this) {
          if (value._owner.parent != this) {
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
            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 '
                'first render object.'
              ),
              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 '
                'debugAdoptSize at that time.'
              ),
              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 '
                'approach should be used.'
              ),
            ]);
1797 1798
          }
          if (!value._canBeUsedByParent) {
1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809
            throw FlutterError.fromParts(<DiagnosticsNode>[
              ErrorSummary('A child\'s size was used without setting parentUsesSize.'),
              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 '
                'size will not be used by the parent.'
1810
              ),
1811
            ]);
1812 1813 1814
          }
        }
      }
1815
      result = _DebugSize(value, this, debugCanParentUseSize);
1816
      return true;
1817
    }());
1818
    return result;
1819 1820
  }

1821
  @override
1822
  Rect get semanticBounds => Offset.zero & size;
Hixie's avatar
Hixie committed
1823

1824
  @override
1825 1826 1827 1828 1829
  void debugResetSize() {
    // updates the value of size._canBeUsedByParent if necessary
    size = size;
  }

1830 1831 1832 1833 1834 1835
  Map<TextBaseline, double> _cachedBaselines;
  static bool _debugDoingBaseline = false;
  static bool _debugSetDoingBaseline(bool value) {
    _debugDoingBaseline = value;
    return true;
  }
Adam Barth's avatar
Adam Barth committed
1836 1837 1838 1839 1840 1841 1842 1843

  /// 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
1844
  /// (i.e., the height of the box) unless the caller passes true
Adam Barth's avatar
Adam Barth committed
1845 1846
  /// for `onlyReal`, in which case the function returns null.
  ///
1847 1848 1849
  /// 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.
1850 1851 1852
  ///
  /// When implementing a [RenderBox] subclass, to override the baseline
  /// computation, override [computeDistanceToActualBaseline].
1853
  double getDistanceToBaseline(TextBaseline baseline, { bool onlyReal = false }) {
1854
    assert(!_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
1855
    assert(!debugNeedsLayout);
1856
    assert(() {
1857
      final RenderObject parent = this.parent as RenderObject;
1858
      if (owner.debugDoingLayout)
1859
        return (RenderObject.debugActiveLayout == parent) && parent.debugDoingThisLayout;
1860
      if (owner.debugDoingPaint)
1861 1862
        return ((RenderObject.debugActivePaint == parent) && parent.debugDoingThisPaint) ||
               ((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
1863
      assert(parent == this.parent);
1864
      return false;
1865
    }());
1866
    assert(_debugSetDoingBaseline(true));
1867
    final double result = getDistanceToActualBaseline(baseline);
1868 1869 1870 1871 1872
    assert(_debugSetDoingBaseline(false));
    if (result == null && !onlyReal)
      return size.height;
    return result;
  }
Adam Barth's avatar
Adam Barth committed
1873 1874 1875 1876 1877 1878

  /// 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.
1879
  @protected
1880
  @mustCallSuper
1881
  double getDistanceToActualBaseline(TextBaseline baseline) {
1882
    assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
1883
    _cachedBaselines ??= <TextBaseline, double>{};
1884 1885 1886
    _cachedBaselines.putIfAbsent(baseline, () => computeDistanceToActualBaseline(baseline));
    return _cachedBaselines[baseline];
  }
Adam Barth's avatar
Adam Barth committed
1887 1888 1889 1890 1891

  /// 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.
  ///
1892 1893 1894
  /// 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
1895
  ///
1896
  /// Subclasses should override this method to supply the distances to their
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
  /// 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).
1912
  @protected
1913
  double computeDistanceToActualBaseline(TextBaseline baseline) {
1914
    assert(_debugDoingBaseline, 'Please see the documentation for computeDistanceToActualBaseline for the required calling conventions of this method.');
1915 1916 1917
    return null;
  }

1918
  /// The box constraints most recently received from the parent.
1919
  @override
1920
  BoxConstraints get constraints => super.constraints as BoxConstraints;
1921 1922

  @override
1923
  void debugAssertDoesMeetConstraints() {
1924
    assert(constraints != null);
1925 1926
    assert(() {
      if (!hasSize) {
1927
        assert(!debugNeedsLayout); // this is called in the size= setter during layout, but in that case we have a size
1928
        DiagnosticsNode contract;
1929
        if (sizedByParent)
1930
          contract = ErrorDescription('Because this RenderBox has sizedByParent set to true, it must set its size in performResize().');
1931
        else
1932 1933 1934 1935 1936
          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.'),
1937
          DiagnosticsProperty<RenderBox>('The RenderBox in question is', this, style: DiagnosticsTreeStyle.errorProperty),
1938
        ]);
1939
      }
1940
      // verify that the size is not infinite
1941
      if (!_size.isFinite) {
1942 1943 1944 1945 1946 1947
        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 '
            'that allows its children to pick their own size.'
1948
          ),
1949
        ];
1950 1951 1952
        if (!constraints.hasBoundedWidth) {
          RenderBox node = this;
          while (!node.constraints.hasBoundedWidth && node.parent is RenderBox)
1953
            node = node.parent as RenderBox;
1954 1955

          information.add(node.describeForError('The nearest ancestor providing an unbounded width constraint is'));
1956
        }
1957 1958 1959
        if (!constraints.hasBoundedHeight) {
          RenderBox node = this;
          while (!node.constraints.hasBoundedHeight && node.parent is RenderBox)
1960
            node = node.parent as RenderBox;
1961

1962
          information.add(node.describeForError('The nearest ancestor providing an unbounded height constraint is'));
1963
        }
1964 1965 1966 1967 1968 1969
        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.'),
        ]);
1970
     }
1971 1972
      // verify that the size is within the constraints
      if (!constraints.isSatisfiedBy(_size)) {
1973 1974 1975 1976 1977 1978 1979 1980 1981
        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 '
            'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=BUG.md'
          ),
        ]);
1982
      }
1983
      if (debugCheckIntrinsicSizes) {
1984
        // verify that the intrinsics are sane
1985 1986
        assert(!RenderObject.debugCheckingIntrinsics);
        RenderObject.debugCheckingIntrinsics = true;
1987
        final List<DiagnosticsNode> failures = <DiagnosticsNode>[];
1988 1989 1990 1991

        double testIntrinsic(double function(double extent), String name, double constraint) {
          final double result = function(constraint);
          if (result < 0) {
1992
            failures.add(ErrorDescription(' * $name($constraint) returned a negative value: $result'));
1993 1994
          }
          if (!result.isFinite) {
1995
            failures.add(ErrorDescription(' * $name($constraint) returned a non-finite value: $result'));
1996 1997
          }
          return result;
1998
        }
1999

2000 2001 2002 2003
        void testIntrinsicsForValues(double getMin(double extent), double getMax(double extent), String name, double constraint) {
          final double min = testIntrinsic(getMin, 'getMinIntrinsic$name', constraint);
          final double max = testIntrinsic(getMax, 'getMaxIntrinsic$name', constraint);
          if (min > max) {
2004
            failures.add(ErrorDescription(' * getMinIntrinsic$name($constraint) returned a larger value ($min) than getMaxIntrinsic$name($constraint) ($max)'));
2005
          }
2006
        }
2007

2008 2009
        testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', double.infinity);
        testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', double.infinity);
2010
        if (constraints.hasBoundedWidth)
2011
          testIntrinsicsForValues(getMinIntrinsicWidth, getMaxIntrinsicWidth, 'Width', constraints.maxHeight);
2012
        if (constraints.hasBoundedHeight)
2013
          testIntrinsicsForValues(getMinIntrinsicHeight, getMaxIntrinsicHeight, 'Height', constraints.maxWidth);
2014

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

2017 2018
        RenderObject.debugCheckingIntrinsics = false;
        if (failures.isNotEmpty) {
2019 2020 2021
          // 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.'),
2022 2023 2024
            ErrorDescription('The following ${failures.length > 1 ? "failures" : "failure"} was detected:'), // should this be tagged as an error or not?
            ...failures,
            ErrorHint(
2025 2026
              'If you are not writing your own RenderBox subclass, then this is not\n'
              'your fault. Contact support: https://github.com/flutter/flutter/issues/new?template=BUG.md'
2027 2028
            ),
          ]);
2029
        }
2030 2031
      }
      return true;
2032
    }());
2033 2034
  }

2035
  @override
2036
  void markNeedsLayout() {
2037 2038 2039 2040 2041 2042 2043 2044 2045
    if ((_cachedBaselines != null && _cachedBaselines.isNotEmpty) ||
        (_cachedIntrinsicDimensions != null && _cachedIntrinsicDimensions.isNotEmpty)) {
      // 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();
2046
      if (parent is RenderObject) {
2047 2048 2049
        markParentNeedsLayout();
        return;
      }
2050 2051 2052
    }
    super.markNeedsLayout();
  }
2053 2054

  @override
2055
  void performResize() {
2056
    // default behavior for subclasses that have sizedByParent = true
2057
    size = constraints.smallest;
2058
    assert(size.isFinite);
2059
  }
2060 2061

  @override
2062
  void performLayout() {
2063 2064
    assert(() {
      if (!sizedByParent) {
2065 2066 2067 2068 2069 2070
        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 '
            'so that performResize() sizes the render object.'
2071
          ),
2072
        ]);
2073 2074
      }
      return true;
2075
    }());
2076 2077
  }

2078
  /// Determines the set of render objects located at the given position.
Adam Barth's avatar
Adam Barth committed
2079
  ///
2080 2081 2082 2083
  /// 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
2084
  ///
2085 2086 2087 2088
  /// 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.
2089
  ///
2090 2091 2092 2093 2094
  /// 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`.
2095
  ///
2096 2097 2098 2099 2100 2101
  /// 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.
2102
  bool hitTest(BoxHitTestResult result, { @required Offset position }) {
2103 2104
    assert(() {
      if (!hasSize) {
2105
        if (debugNeedsLayout) {
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118
          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(
              'Unfortunately, this object\'s geometry is not known at this time, '
              'probably because it has never been laid out. '
              'This means it cannot be accurately hit-tested.'
            ),
            ErrorHint(
              'If you are trying '
              'to perform a hit test during the layout phase itself, make sure '
              'you only hit test nodes that have completed layout (e.g. the node\'s '
              'children, after their layout() method has been called).'
2119
            ),
2120
          ]);
2121
        }
2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134
        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, '
            'its size is not set.'
          ),
          ErrorHint(
            'A RenderBox object must have an '
            'explicit size before it can be hit-tested. Make sure '
            'that the RenderBox in question sets its size during layout.'
          ),
        ]);
2135 2136
      }
      return true;
2137
    }());
2138
    if (_size.contains(position)) {
Adam Barth's avatar
Adam Barth committed
2139
      if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
2140
        result.add(BoxHitTestEntry(this, position));
Adam Barth's avatar
Adam Barth committed
2141 2142
        return true;
      }
2143 2144
    }
    return false;
2145
  }
Adam Barth's avatar
Adam Barth committed
2146

2147
  /// Override this method if this render object can be hit even if its
2148
  /// children were not hit.
2149
  ///
2150 2151 2152 2153 2154
  /// 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.
  ///
2155 2156
  /// Used by [hitTest]. If you override [hitTest] and do not call this
  /// function, then you don't need to implement this function.
2157
  @protected
2158
  bool hitTestSelf(Offset position) => false;
Adam Barth's avatar
Adam Barth committed
2159

2160
  /// Override this method to check whether any children are located at the
2161
  /// given position.
Adam Barth's avatar
Adam Barth committed
2162
  ///
2163
  /// Typically children should be hit-tested in reverse paint order so that
Adam Barth's avatar
Adam Barth committed
2164 2165
  /// hit tests at locations where children overlap hit the child that is
  /// visually "on top" (i.e., paints later).
2166
  ///
2167 2168 2169 2170 2171
  /// 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.
  ///
2172 2173 2174 2175 2176
  /// If transforming is necessary, [HitTestResult.addWithPaintTransform],
  /// [HitTestResult.addWithPaintOffset], or [HitTestResult.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`.
2177
  ///
2178 2179
  /// Used by [hitTest]. If you override [hitTest] and do not call this
  /// function, then you don't need to implement this function.
2180
  @protected
2181
  bool hitTestChildren(BoxHitTestResult result, { Offset position }) => false;
2182

Adam Barth's avatar
Adam Barth committed
2183
  /// Multiply the transform from the parent's coordinate system to this box's
2184
  /// coordinate system into the given transform.
Adam Barth's avatar
Adam Barth committed
2185 2186 2187 2188
  ///
  /// 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.
2189
  ///
2190
  /// The [RenderBox] implementation takes care of adjusting the matrix for the
2191 2192
  /// position of the given child as determined during layout and stored on the
  /// child's [parentData] in the [BoxParentData.offset] field.
2193
  @override
2194
  void applyPaintTransform(RenderObject child, Matrix4 transform) {
2195
    assert(child != null);
2196
    assert(child.parent == this);
2197 2198
    assert(() {
      if (child.parentData is! BoxParentData) {
2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209
        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 '
            'subclass used by its children (which apparently is ${child.parentData.runtimeType}).'
2210
          ),
2211
        ]);
2212 2213
      }
      return true;
2214
    }());
2215
    final BoxParentData childParentData = child.parentData as BoxParentData;
2216
    final Offset offset = childParentData.offset;
2217
    transform.translate(offset.dx, offset.dy);
2218 2219
  }

2220
  /// Convert the given point from the global coordinate system in logical pixels
2221
  /// to the local coordinate system for this box.
2222
  ///
2223 2224 2225
  /// This method will un-project the point from the screen onto the widget,
  /// which makes it different from [MatrixUtils.transformPoint].
  ///
2226
  /// If the transform from global coordinates to local coordinates is
2227
  /// degenerate, this function returns [Offset.zero].
2228 2229 2230 2231
  ///
  /// 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.
2232 2233
  ///
  /// This method is implemented in terms of [getTransformTo].
2234
  Offset globalToLocal(Offset point, { RenderObject ancestor }) {
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246
    // 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.
2247
    final Matrix4 transform = getTransformTo(ancestor);
2248
    final double det = transform.invert();
2249
    if (det == 0.0)
2250
      return Offset.zero;
2251 2252 2253 2254 2255 2256
    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);
2257 2258
  }

2259
  /// Convert the given point from the local coordinate system for this box to
2260
  /// the global coordinate system in logical pixels.
2261 2262 2263 2264
  ///
  /// 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.
2265 2266
  ///
  /// This method is implemented in terms of [getTransformTo].
2267
  Offset localToGlobal(Offset point, { RenderObject ancestor }) {
2268
    return MatrixUtils.transformPoint(getTransformTo(ancestor), point);
2269 2270
  }

2271
  /// Returns a rectangle that contains all the pixels painted by this box.
Adam Barth's avatar
Adam Barth committed
2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283
  ///
  /// 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.
2284
  @override
2285
  Rect get paintBounds => Offset.zero & size;
Adam Barth's avatar
Adam Barth committed
2286

2287
  /// Override this method to handle pointer events that hit this render object.
2288 2289 2290 2291
  ///
  /// 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.)
2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302
  ///
  /// If you override this, consider calling [debugHandleEvent] as follows, so
  /// that you can support [debugPaintPointersEnabled]:
  ///
  /// ```dart
  /// @override
  /// void handleEvent(PointerEvent event, HitTestEntry entry) {
  ///   assert(debugHandleEvent(event, entry));
  ///   // ... handle the event ...
  /// }
  /// ```
2303
  @override
2304
  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
2305
    super.handleEvent(event, entry);
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325
  }

  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) {
2326 2327
    assert(() {
      if (debugPaintPointersEnabled) {
Ian Hickson's avatar
Ian Hickson committed
2328
        if (event is PointerDownEvent) {
2329
          _debugActivePointers += 1;
Ian Hickson's avatar
Ian Hickson committed
2330
        } else if (event is PointerUpEvent || event is PointerCancelEvent) {
2331
          _debugActivePointers -= 1;
Ian Hickson's avatar
Ian Hickson committed
2332
        }
2333 2334 2335
        markNeedsPaint();
      }
      return true;
2336
    }());
2337
    return true;
2338 2339
  }

2340
  @override
2341
  void debugPaint(PaintingContext context, Offset offset) {
2342 2343 2344 2345 2346 2347 2348 2349
    assert(() {
      if (debugPaintSizeEnabled)
        debugPaintSize(context, offset);
      if (debugPaintBaselinesEnabled)
        debugPaintBaselines(context, offset);
      if (debugPaintPointersEnabled)
        debugPaintPointers(context, offset);
      return true;
2350
    }());
2351
  }
2352 2353 2354 2355

  /// In debug mode, paints a border around this render box.
  ///
  /// Called for every [RenderBox] when [debugPaintSizeEnabled] is true.
2356
  @protected
2357
  void debugPaintSize(PaintingContext context, Offset offset) {
2358
    assert(() {
2359
      final Paint paint = Paint()
2360
       ..style = PaintingStyle.stroke
2361
       ..strokeWidth = 1.0
2362
       ..color = const Color(0xFF00FFFF);
2363 2364
      context.canvas.drawRect((offset & size).deflate(0.5), paint);
      return true;
2365
    }());
2366
  }
2367 2368 2369 2370

  /// In debug mode, paints a line for each baseline.
  ///
  /// Called for every [RenderBox] when [debugPaintBaselinesEnabled] is true.
2371
  @protected
2372
  void debugPaintBaselines(PaintingContext context, Offset offset) {
2373
    assert(() {
2374
      final Paint paint = Paint()
2375
       ..style = PaintingStyle.stroke
2376 2377 2378
       ..strokeWidth = 0.25;
      Path path;
      // ideographic baseline
2379
      final double baselineI = getDistanceToBaseline(TextBaseline.ideographic, onlyReal: true);
2380
      if (baselineI != null) {
2381
        paint.color = const Color(0xFFFFD000);
2382
        path = Path();
2383 2384 2385 2386 2387
        path.moveTo(offset.dx, offset.dy + baselineI);
        path.lineTo(offset.dx + size.width, offset.dy + baselineI);
        context.canvas.drawPath(path, paint);
      }
      // alphabetic baseline
2388
      final double baselineA = getDistanceToBaseline(TextBaseline.alphabetic, onlyReal: true);
2389
      if (baselineA != null) {
2390
        paint.color = const Color(0xFF00FF00);
2391
        path = Path();
2392 2393 2394 2395 2396
        path.moveTo(offset.dx, offset.dy + baselineA);
        path.lineTo(offset.dx + size.width, offset.dy + baselineA);
        context.canvas.drawPath(path, paint);
      }
      return true;
2397
    }());
2398
  }
2399

2400 2401
  /// In debug mode, paints a rectangle if this render box has counted more
  /// pointer downs than pointer up events.
2402 2403
  ///
  /// Called for every [RenderBox] when [debugPaintPointersEnabled] is true.
2404 2405 2406
  ///
  /// By default, events are not counted. For details on how to ensure that
  /// events are counted for your class, see [debugHandleEvent].
2407
  @protected
2408
  void debugPaintPointers(PaintingContext context, Offset offset) {
2409 2410
    assert(() {
      if (_debugActivePointers > 0) {
2411 2412
        final Paint paint = Paint()
         ..color = Color(0x00BBBB | ((0x04000000 * depth) & 0xFF000000));
2413 2414 2415
        context.canvas.drawRect(offset & size, paint);
      }
      return true;
2416
    }());
2417
  }
2418

2419
  @override
2420 2421
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2422
    properties.add(DiagnosticsProperty<Size>('size', _size, missingIfNull: true));
2423
  }
2424 2425
}

2426 2427
/// A mixin that provides useful default behaviors for boxes with children
/// managed by the [ContainerRenderObjectMixin] mixin.
Adam Barth's avatar
Adam Barth committed
2428 2429 2430 2431
///
/// By convention, this class doesn't override any members of the superclass.
/// Instead, it provides helpful functions that subclasses can call as
/// appropriate.
2432
mixin RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, ParentDataType extends ContainerBoxParentData<ChildType>> implements ContainerRenderObjectMixin<ChildType, ParentDataType> {
2433
  /// Returns the baseline of the first child with a baseline.
Adam Barth's avatar
Adam Barth committed
2434 2435 2436
  ///
  /// Useful when the children are displayed vertically in the same order they
  /// appear in the child list.
2437
  double defaultComputeDistanceToFirstActualBaseline(TextBaseline baseline) {
2438
    assert(!debugNeedsLayout);
Adam Barth's avatar
Adam Barth committed
2439
    ChildType child = firstChild;
2440
    while (child != null) {
2441
      final ParentDataType childParentData = child.parentData as ParentDataType;
2442
      final double result = child.getDistanceToActualBaseline(baseline);
2443
      if (result != null)
2444
        return result + childParentData.offset.dy;
Hixie's avatar
Hixie committed
2445
      child = childParentData.nextSibling;
2446 2447 2448 2449
    }
    return null;
  }

2450
  /// Returns the minimum baseline value among every child.
Adam Barth's avatar
Adam Barth committed
2451 2452 2453
  ///
  /// Useful when the vertical position of the children isn't determined by the
  /// order in the child list.
2454
  double defaultComputeDistanceToHighestActualBaseline(TextBaseline baseline) {
2455
    assert(!debugNeedsLayout);
2456
    double result;
Adam Barth's avatar
Adam Barth committed
2457
    ChildType child = firstChild;
2458
    while (child != null) {
2459
      final ParentDataType childParentData = child.parentData as ParentDataType;
2460 2461
      double candidate = child.getDistanceToActualBaseline(baseline);
      if (candidate != null) {
2462
        candidate += childParentData.offset.dy;
2463 2464 2465 2466 2467
        if (result != null)
          result = math.min(result, candidate);
        else
          result = candidate;
      }
Hixie's avatar
Hixie committed
2468
      child = childParentData.nextSibling;
2469 2470 2471 2472
    }
    return result;
  }

2473
  /// Performs a hit test on each child by walking the child list backwards.
Adam Barth's avatar
Adam Barth committed
2474 2475 2476
  ///
  /// Stops walking once after the first child reports that it contains the
  /// given point. Returns whether any children contain the given point.
2477 2478 2479 2480 2481
  ///
  /// See also:
  ///
  ///  * [defaultPaint], which paints the children appropriate for this
  ///    hit-testing strategy.
2482
  bool defaultHitTestChildren(BoxHitTestResult result, { Offset position }) {
2483 2484 2485
    // the x, y parameters have the top left of the node's box as the origin
    ChildType child = lastChild;
    while (child != null) {
2486
      final ParentDataType childParentData = child.parentData as ParentDataType;
2487 2488 2489 2490 2491 2492 2493 2494 2495
      final bool isHit = result.addWithPaintOffset(
        offset: childParentData.offset,
        position: position,
        hitTest: (BoxHitTestResult result, Offset transformed) {
          assert(transformed == position - childParentData.offset);
          return child.hitTest(result, position: transformed);
        },
      );
      if (isHit)
2496
        return true;
Hixie's avatar
Hixie committed
2497
      child = childParentData.previousSibling;
2498
    }
2499
    return false;
2500 2501
  }

2502
  /// Paints each child by walking the child list forwards.
2503 2504 2505 2506 2507
  ///
  /// See also:
  ///
  ///  * [defaultHitTestChildren], which implements hit-testing of the children
  ///    in a manner appropriate for this painting strategy.
2508
  void defaultPaint(PaintingContext context, Offset offset) {
Adam Barth's avatar
Adam Barth committed
2509
    ChildType child = firstChild;
2510
    while (child != null) {
2511
      final ParentDataType childParentData = child.parentData as ParentDataType;
Adam Barth's avatar
Adam Barth committed
2512
      context.paintChild(child, childParentData.offset + offset);
Hixie's avatar
Hixie committed
2513
      child = childParentData.nextSibling;
2514 2515
    }
  }
Adam Barth's avatar
Adam Barth committed
2516

2517 2518 2519 2520 2521
  /// 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
2522
  List<ChildType> getChildrenAsList() {
2523
    final List<ChildType> result = <ChildType>[];
Adam Barth's avatar
Adam Barth committed
2524 2525
    RenderBox child = firstChild;
    while (child != null) {
2526 2527
      final ParentDataType childParentData = child.parentData as ParentDataType;
      result.add(child as ChildType);
Adam Barth's avatar
Adam Barth committed
2528 2529 2530 2531
      child = childParentData.nextSibling;
    }
    return result;
  }
2532
}