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