flex.dart 25 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;

7 8
import 'box.dart';
import 'object.dart';
9

Adam Barth's avatar
Adam Barth committed
10
/// Parent data for use with [RenderFlex].
Hixie's avatar
Hixie committed
11
class FlexParentData extends ContainerBoxParentDataMixin<RenderBox> {
12 13 14 15 16 17
  /// The flex factor to use for this child
  ///
  /// If null, the child is inflexible and determines its own size. If non-null,
  /// the child is flexible and its extent in the main axis is determined by
  /// dividing the free space (after placing the inflexible children)
  /// according to the flex factors of the flexible children.
18 19
  int flex;

20
  @override
21 22 23
  String toString() => '${super.toString()}; flex=$flex';
}

24 25 26 27 28 29 30
/// The direction in which the box should flex
enum FlexDirection {
  /// Children are arranged horizontally, from left to right
  horizontal,
  /// Children are arranged vertically, from top to bottom
  vertical
}
31

32
/// How the children should be placed along the main axis in a flex layout.
33
enum MainAxisAlignment {
34
  /// Place the children as close to the start of the main axis as possible.
35
  start,
36 37

  /// Place the children as close to the end of the main axis as possible.
38
  end,
39 40

  /// Place the children as close to the middle of the main axis as possible.
41
  center,
42 43

  /// Place the free space evenly between the children.
44
  spaceBetween,
45 46

  /// Place the free space evenly between the children as well as half of that space before and after the first and last child.
47
  spaceAround,
48 49 50 51

  /// Place the free space evenly between the children as well as before and after the first and last child.
  spaceEvenly,

52 53
  /// Do not expand to fill the free space. None of the children may specify a flex factor.
  collapse,
54 55
}

56
/// How the children should be placed along the cross axis in a flex layout.
57
enum CrossAxisAlignment {
58
  /// Place the children as close to the start of the cross axis as possible.
59
  start,
60 61

  /// Place the children as close to the end of the cross axis as possible.
62
  end,
63 64

  /// Place the children as close to the middle of the cross axis as possible.
65
  center,
66 67

  /// Require the children to fill the cross axis.
68
  stretch,
69 70

  /// Place the children along the cross axis such that their baselines match.
71
  baseline,
72 73
}

74
typedef double _ChildSizingFunction(RenderBox child, double extent);
75

76 77 78 79 80 81 82 83 84 85
/// Implements the flex layout algorithm
///
/// In flex layout, children are arranged linearly along the main axis (either
/// horizontally or vertically). First, inflexible children (those with a null
/// flex factor) are allocated space along the main axis. If the flex is given
/// unlimited space in the main axis, the flex sizes its main axis to the total
/// size of the inflexible children along the main axis and forbids flexible
/// children. Otherwise, the flex expands to the maximum max-axis size and the
/// remaining space along is divided among the flexible children according to
/// their flex factors. Any remaining free space (i.e., if there aren't any
86
/// flexible children) is allocated according to the [mainAxisAlignment] property.
87 88 89
///
/// In the cross axis, children determine their own size. The flex then sizes
/// its cross axis to fix the largest of its children. The children are then
90
/// positioned along the cross axis according to the [crossAxisAlignment] property.
91 92
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
                                        RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData> {
93 94 95 96
  /// Creates a flex render object.
  ///
  /// By default, the flex layout is horizontal and children are aligned to the
  /// start of the main axis and the center of the cross axis.
97
  RenderFlex({
98
    List<RenderBox> children,
99
    FlexDirection direction: FlexDirection.horizontal,
100 101
    MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start,
    CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center,
102
    TextBaseline textBaseline
103
  }) : _direction = direction,
104 105
       _mainAxisAlignment = mainAxisAlignment,
       _crossAxisAlignment = crossAxisAlignment,
106
       _textBaseline = textBaseline {
107 108 109
    addAll(children);
  }

110
  /// The direction to use as the main axis.
111
  FlexDirection get direction => _direction;
112
  FlexDirection _direction;
113
  set direction (FlexDirection value) {
114 115 116 117 118 119
    if (_direction != value) {
      _direction = value;
      markNeedsLayout();
    }
  }

120
  /// How the children should be placed along the main axis.
121 122
  MainAxisAlignment get mainAxisAlignment => _mainAxisAlignment;
  MainAxisAlignment _mainAxisAlignment;
123
  set mainAxisAlignment (MainAxisAlignment value) {
124 125
    if (_mainAxisAlignment != value) {
      _mainAxisAlignment = value;
126 127 128 129
      markNeedsLayout();
    }
  }

130
  /// How the children should be placed along the cross axis.
131 132
  CrossAxisAlignment get crossAxisAlignment => _crossAxisAlignment;
  CrossAxisAlignment _crossAxisAlignment;
133
  set crossAxisAlignment (CrossAxisAlignment value) {
134 135
    if (_crossAxisAlignment != value) {
      _crossAxisAlignment = value;
136 137 138 139
      markNeedsLayout();
    }
  }

140
  /// If aligning items according to their baseline, which baseline to use.
141
  TextBaseline get textBaseline => _textBaseline;
142
  TextBaseline _textBaseline;
143
  set textBaseline (TextBaseline value) {
144 145 146 147 148 149
    if (_textBaseline != value) {
      _textBaseline = value;
      markNeedsLayout();
    }
  }

150
  /// Set during layout if overflow occurred on the main axis.
151
  double _overflow;
152

153
  @override
154
  void setupParentData(RenderBox child) {
155 156
    if (child.parentData is! FlexParentData)
      child.parentData = new FlexParentData();
157 158
  }

159 160 161 162 163
  double _getIntrinsicSize({
    FlexDirection sizingDirection,
    double extent, // the extent in the direction that isn't the sizing direction
    _ChildSizingFunction childSize // a method to find the size in the sizing direction
  }) {
164 165 166 167 168 169 170 171 172 173 174 175
    if (_direction == sizingDirection) {
      // INTRINSIC MAIN SIZE
      // Intrinsic main size is the smallest size the flex container can take
      // while maintaining the min/max-content contributions of its flex items.
      double totalFlex = 0.0;
      double inflexibleSpace = 0.0;
      double maxFlexFractionSoFar = 0.0;
      RenderBox child = firstChild;
      while (child != null) {
        int flex = _getFlex(child);
        totalFlex += flex;
        if (flex > 0) {
176
          double flexFraction = childSize(child, extent) / _getFlex(child);
177 178
          maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction);
        } else {
179
          inflexibleSpace += childSize(child, extent);
180
        }
Hixie's avatar
Hixie committed
181 182
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
183
      }
184
      return maxFlexFractionSoFar * totalFlex + inflexibleSpace;
185 186
    } else {
      // INTRINSIC CROSS SIZE
187 188 189 190
      // Intrinsic cross size is the max of the intrinsic cross sizes of the
      // children, after the flexible children are fit into the available space,
      // with the children sized using their max intrinsic dimensions.
      // TODO(ianh): Support baseline alignment.
191

192 193
      // Get inflexible space using the max intrinsic dimensions of fixed children in the main direction.
      double availableMainSpace = extent;
194 195 196 197 198 199 200 201 202 203 204 205
      int totalFlex = 0;
      double inflexibleSpace = 0.0;
      double maxCrossSize = 0.0;
      RenderBox child = firstChild;
      while (child != null) {
        int flex = _getFlex(child);
        totalFlex += flex;
        double mainSize;
        double crossSize;
        if (flex == 0) {
          switch (_direction) {
              case FlexDirection.horizontal:
206 207
                mainSize = child.getMaxIntrinsicWidth(double.INFINITY);
                crossSize = childSize(child, mainSize);
208 209
                break;
              case FlexDirection.vertical:
210 211
                mainSize = child.getMaxIntrinsicHeight(double.INFINITY);
                crossSize = childSize(child, mainSize);
212 213 214 215 216
                break;
          }
          inflexibleSpace += mainSize;
          maxCrossSize = math.max(maxCrossSize, crossSize);
        }
Hixie's avatar
Hixie committed
217 218
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
219 220
      }

221
      // Determine the spacePerFlex by allocating the remaining available space.
222 223 224
      // When you're overconstrained spacePerFlex can be negative.
      double spacePerFlex = math.max(0.0,
          (availableMainSpace - inflexibleSpace) / totalFlex);
225

226
      // Size remaining (flexible) items, find the maximum cross size.
227 228 229
      child = firstChild;
      while (child != null) {
        int flex = _getFlex(child);
230 231
        if (flex > 0)
          maxCrossSize = math.max(maxCrossSize, childSize(child, spacePerFlex * flex));
Hixie's avatar
Hixie committed
232 233
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
234 235
      }

236
      return maxCrossSize;
237 238 239
    }
  }

240
  @override
241
  double getMinIntrinsicWidth(double height) {
242 243
    return _getIntrinsicSize(
      sizingDirection: FlexDirection.horizontal,
244 245
      extent: height,
      childSize: (RenderBox child, double extent) => child.getMinIntrinsicWidth(extent)
246 247 248
    );
  }

249
  @override
250
  double getMaxIntrinsicWidth(double height) {
251 252
    return _getIntrinsicSize(
      sizingDirection: FlexDirection.horizontal,
253 254
      extent: height,
      childSize: (RenderBox child, double extent) => child.getMaxIntrinsicWidth(extent)
255 256 257
    );
  }

258
  @override
259
  double getMinIntrinsicHeight(double width) {
260 261
    return _getIntrinsicSize(
      sizingDirection: FlexDirection.vertical,
262 263
      extent: width,
      childSize: (RenderBox child, double extent) => child.getMinIntrinsicHeight(extent)
264 265 266
    );
  }

267
  @override
268
  double getMaxIntrinsicHeight(double width) {
269 270
    return _getIntrinsicSize(
      sizingDirection: FlexDirection.vertical,
271 272 273
      extent: width,
      childSize: (RenderBox child, double extent) => child.getMaxIntrinsicHeight(extent)
    );
274 275
  }

276
  @override
277 278 279 280 281 282 283
  double computeDistanceToActualBaseline(TextBaseline baseline) {
    if (_direction == FlexDirection.horizontal)
      return defaultComputeDistanceToHighestActualBaseline(baseline);
    return defaultComputeDistanceToFirstActualBaseline(baseline);
  }

  int _getFlex(RenderBox child) {
Hixie's avatar
Hixie committed
284 285
    final FlexParentData childParentData = child.parentData;
    return childParentData.flex != null ? childParentData.flex : 0;
286 287 288 289 290 291 292 293 294 295
  }

  double _getCrossSize(RenderBox child) {
    return (_direction == FlexDirection.horizontal) ? child.size.height : child.size.width;
  }

  double _getMainSize(RenderBox child) {
    return (_direction == FlexDirection.horizontal) ? child.size.width : child.size.height;
  }

296
  @override
297
  void performLayout() {
298
    // Determine used flex factor, size inflexible items, calculate free space.
299 300 301
    int totalFlex = 0;
    int totalChildren = 0;
    assert(constraints != null);
302
    final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.constrainWidth() : constraints.constrainHeight();
303
    final bool canFlex = mainSize < double.INFINITY && mainAxisAlignment != MainAxisAlignment.collapse;
304 305
    double crossSize = 0.0;  // This is determined as we lay out the children
    double freeSpace = canFlex ? mainSize : 0.0;
306 307
    RenderBox child = firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
308
      final FlexParentData childParentData = child.parentData;
309 310 311
      totalChildren++;
      int flex = _getFlex(child);
      if (flex > 0) {
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
        assert(() {
          final String identity = _direction == FlexDirection.horizontal ? 'row' : 'column';
          final String axis = _direction == FlexDirection.horizontal ? 'horizontal' : 'vertical';
          final String dimension = _direction == FlexDirection.horizontal ? 'width' : 'height';
          String error, message;
          String addendum = '';
          if (mainAxisAlignment == MainAxisAlignment.collapse) {
            error = 'RenderFlex children have non-zero flex but mainAxisAlignment is set to "collapse".';
            message = 'The MainAxisAlignment.collapse value indicates that the $identity is to shrink-wrap its children '
                      'along the $axis axis. Setting a flex on a child (e.g. using a Flexible) indicates that the '
                      'child is to expand to fill the remaining space in the $axis direction.';
          } else if (mainSize == double.INFINITY) {
            error = 'RenderFlex children have non-zero flex but incoming $dimension constraints are unbounded.';
            message = 'When a $identity is in a parent that does not provide a finite $dimension constraint, for example '
                      'if it is in a $axis scrollable, it will try to shrink-wrap its children along the $axis '
                      'axis. Setting a flex on a child (e.g. using a Flexible) indicates that the child is to '
                      'expand to fill the remaining space in the $axis direction.';
            StringBuffer information = new StringBuffer();
            RenderBox node = this;
            switch (_direction) {
              case FlexDirection.horizontal:
                while (!node.constraints.hasBoundedWidth && node.parent is RenderBox)
                  node = node.parent;
                if (!node.constraints.hasBoundedWidth)
                  node = null;
                break;
              case FlexDirection.vertical:
                while (!node.constraints.hasBoundedHeight && node.parent is RenderBox)
                  node = node.parent;
                if (!node.constraints.hasBoundedHeight)
                  node = null;
                break;
            }
            if (node != null) {
              information.writeln('The nearest ancestor providing an unbounded width constraint is:');
              information.writeln('  $node');
              List<String> description = <String>[];
              node.debugFillDescription(description);
              for (String line in description)
                information.writeln('  $line');
            }
            information.writeln('See also: https://flutter.io/layout/');
            addendum = information.toString();
          } else {
            return true;
          }
          throw new FlutterError(
            '$error\n'
            '$message\n'
            'These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child '
            'cannot simultaneously expand to fit its parent.\n'
            'The affected RenderFlex is:\n'
            '  $this\n'
            'The creator information is set to:\n'
            '  $debugCreator\n'
            '$addendum'
            'If this message did not help you determine the problem, consider using debugDumpRenderTree():\n'
            '  https://flutter.io/debugging/#rendering-layer\n'
            '  http://docs.flutter.io/flutter/rendering/debugDumpRenderTree.html\n'
            'If none of the above helps enough to fix this problem, please don\'t hesitate to file a bug:\n'
            '  https://github.com/flutter/flutter/issues/new'
          );
        });
Hixie's avatar
Hixie committed
375
        totalFlex += childParentData.flex;
376 377
      } else {
        BoxConstraints innerConstraints;
378
        if (crossAxisAlignment == CrossAxisAlignment.stretch) {
379 380
          switch (_direction) {
            case FlexDirection.horizontal:
381
              innerConstraints = new BoxConstraints(minHeight: constraints.maxHeight,
382 383 384
                                                    maxHeight: constraints.maxHeight);
              break;
            case FlexDirection.vertical:
385
              innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
386
                                                    maxWidth: constraints.maxWidth);
387 388 389
              break;
          }
        } else {
390 391 392 393 394 395 396 397
          switch (_direction) {
            case FlexDirection.horizontal:
              innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
              break;
            case FlexDirection.vertical:
              innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
              break;
          }
398 399 400 401 402
        }
        child.layout(innerConstraints, parentUsesSize: true);
        freeSpace -= _getMainSize(child);
        crossSize = math.max(crossSize, _getCrossSize(child));
      }
Hixie's avatar
Hixie committed
403 404
      assert(child.parentData == childParentData);
      child = childParentData.nextSibling;
405
    }
406 407
    _overflow = math.max(0.0, -freeSpace);
    freeSpace = math.max(0.0, freeSpace);
408

409
    // Distribute remaining space to flexible children, and determine baseline.
410
    double maxBaselineDistance = 0.0;
411
    double usedSpace = 0.0;
412
    if (totalFlex > 0 || crossAxisAlignment == CrossAxisAlignment.baseline) {
413 414 415 416 417 418 419
      double spacePerFlex = totalFlex > 0 ? (freeSpace / totalFlex) : 0.0;
      child = firstChild;
      while (child != null) {
        int flex = _getFlex(child);
        if (flex > 0) {
          double spaceForChild = spacePerFlex * flex;
          BoxConstraints innerConstraints;
420
          if (crossAxisAlignment == CrossAxisAlignment.stretch) {
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
            switch (_direction) {
              case FlexDirection.horizontal:
                innerConstraints = new BoxConstraints(minWidth: spaceForChild,
                                                      maxWidth: spaceForChild,
                                                      minHeight: constraints.maxHeight,
                                                      maxHeight: constraints.maxHeight);
                break;
              case FlexDirection.vertical:
                innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
                                                      maxWidth: constraints.maxWidth,
                                                      minHeight: spaceForChild,
                                                      maxHeight: spaceForChild);
                break;
            }
          } else {
            switch (_direction) {
              case FlexDirection.horizontal:
                innerConstraints = new BoxConstraints(minWidth: spaceForChild,
                                                      maxWidth: spaceForChild,
                                                      maxHeight: constraints.maxHeight);
                break;
              case FlexDirection.vertical:
                innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth,
                                                      minHeight: spaceForChild,
                                                      maxHeight: spaceForChild);
                break;
            }
448
          }
449 450 451
          child.layout(innerConstraints, parentUsesSize: true);
          usedSpace += _getMainSize(child);
          crossSize = math.max(crossSize, _getCrossSize(child));
452
        }
453
        if (crossAxisAlignment == CrossAxisAlignment.baseline) {
454 455 456 457 458
          assert(() {
            if (textBaseline == null)
              throw new FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.');
            return true;
          });
459 460 461 462
          double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
          if (distance != null)
            maxBaselineDistance = math.max(maxBaselineDistance, distance);
        }
Hixie's avatar
Hixie committed
463 464
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
465
      }
466 467
    }

468
    // Align items along the main axis.
469 470
    double leadingSpace;
    double betweenSpace;
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
    double remainingSpace;
    if (canFlex) {
      remainingSpace = math.max(0.0, freeSpace - usedSpace);
      switch (_direction) {
        case FlexDirection.horizontal:
          size = constraints.constrain(new Size(mainSize, crossSize));
          crossSize = size.height;
          assert(size.width == mainSize);
          break;
        case FlexDirection.vertical:
          size = constraints.constrain(new Size(crossSize, mainSize));
          crossSize = size.width;
          assert(size.height == mainSize);
          break;
      }
    } else {
      leadingSpace = 0.0;
      betweenSpace = 0.0;
      switch (_direction) {
        case FlexDirection.horizontal:
491
          size = constraints.constrain(new Size(_overflow, crossSize));
492
          crossSize = size.height;
493
          remainingSpace = math.max(0.0, size.width - _overflow);
494 495
          break;
        case FlexDirection.vertical:
496
          size = constraints.constrain(new Size(crossSize, _overflow));
497
          crossSize = size.width;
498
          remainingSpace = math.max(0.0, size.height - _overflow);
499 500 501 502
          break;
      }
      _overflow = 0.0;
    }
503 504 505
    switch (_mainAxisAlignment) {
      case MainAxisAlignment.start:
      case MainAxisAlignment.collapse:
506 507 508
        leadingSpace = 0.0;
        betweenSpace = 0.0;
        break;
509
      case MainAxisAlignment.end:
510 511 512
        leadingSpace = remainingSpace;
        betweenSpace = 0.0;
        break;
513
      case MainAxisAlignment.center:
514 515 516
        leadingSpace = remainingSpace / 2.0;
        betweenSpace = 0.0;
        break;
517
      case MainAxisAlignment.spaceBetween:
518 519 520
        leadingSpace = 0.0;
        betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0;
        break;
521
      case MainAxisAlignment.spaceAround:
522 523 524
        betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0;
        leadingSpace = betweenSpace / 2.0;
        break;
525 526 527 528
      case MainAxisAlignment.spaceEvenly:
        betweenSpace = totalChildren > 0 ? remainingSpace / (totalChildren + 1) : 0.0;
        leadingSpace = betweenSpace;
        break;
529 530
    }

Collin Jackson's avatar
Collin Jackson committed
531
    // Position elements
532 533 534
    double childMainPosition = leadingSpace;
    child = firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
535
      final FlexParentData childParentData = child.parentData;
536
      double childCrossPosition;
537 538 539
      switch (_crossAxisAlignment) {
        case CrossAxisAlignment.stretch:
        case CrossAxisAlignment.start:
540 541
          childCrossPosition = 0.0;
          break;
542
        case CrossAxisAlignment.end:
543 544
          childCrossPosition = crossSize - _getCrossSize(child);
          break;
545
        case CrossAxisAlignment.center:
546 547
          childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0;
          break;
548
        case CrossAxisAlignment.baseline:
549 550
          childCrossPosition = 0.0;
          if (_direction == FlexDirection.horizontal) {
551 552
            assert(textBaseline != null);
            double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
553 554 555 556
            if (distance != null)
              childCrossPosition = maxBaselineDistance - distance;
          }
          break;
557 558 559
      }
      switch (_direction) {
        case FlexDirection.horizontal:
560
          childParentData.offset = new Offset(childMainPosition, childCrossPosition);
561 562
          break;
        case FlexDirection.vertical:
563
          childParentData.offset = new Offset(childCrossPosition, childMainPosition);
564 565 566
          break;
      }
      childMainPosition += _getMainSize(child) + betweenSpace;
Hixie's avatar
Hixie committed
567
      child = childParentData.nextSibling;
568 569 570
    }
  }

571
  @override
Adam Barth's avatar
Adam Barth committed
572 573
  bool hitTestChildren(HitTestResult result, { Point position }) {
    return defaultHitTestChildren(result, position: position);
574 575
  }

576
  @override
577
  void paint(PaintingContext context, Offset offset) {
578
    if (_overflow <= 0.0) {
579
      defaultPaint(context, offset);
580
      return;
581
    }
Collin Jackson's avatar
Collin Jackson committed
582

583
    // We have overflow. Clip it.
584 585
    context.pushClipRect(needsCompositing, offset, Point.origin & size, defaultPaint);

586 587 588 589 590 591 592 593 594 595 596
    assert(() {
      // In debug mode, if you have overflow, we highlight where the
      // overflow would be by painting that area red. Since that is
      // likely to be clipped by an ancestor, we also draw a thick red
      // line at the edge that's overflowing.

      // If you do want clipping, use a RenderClip (Clip in the
      // Widgets library).

      Paint markerPaint = new Paint()..color = const Color(0xE0FF0000);
      Paint highlightPaint = new Paint()..color = const Color(0x7FFF0000);
597
      const double kMarkerSize = 0.1;
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
      Rect markerRect, overflowRect;
      switch(direction) {
        case FlexDirection.horizontal:
          markerRect = offset + new Offset(size.width * (1.0 - kMarkerSize), 0.0) &
                       new Size(size.width * kMarkerSize, size.height);
          overflowRect = offset + new Offset(size.width, 0.0) &
                         new Size(_overflow, size.height);
          break;
        case FlexDirection.vertical:
          markerRect = offset + new Offset(0.0, size.height * (1.0 - kMarkerSize)) &
                       new Size(size.width, size.height * kMarkerSize);
          overflowRect = offset + new Offset(0.0, size.height) &
                         new Size(size.width, _overflow);
          break;
      }
      context.canvas.drawRect(markerRect, markerPaint);
      context.canvas.drawRect(overflowRect, highlightPaint);
      return true;
    });
  }
Collin Jackson's avatar
Collin Jackson committed
618

619
  @override
Hixie's avatar
Hixie committed
620 621
  Rect describeApproximatePaintClip(RenderObject child) => _overflow > 0.0 ? Point.origin & size : null;

622
  @override
623 624
  String toString() {
    String header = super.toString();
625
    if (_overflow is double && _overflow > 0.0)
626 627
      header += ' OVERFLOWING';
    return header;
Collin Jackson's avatar
Collin Jackson committed
628
  }
629

630
  @override
631 632 633
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('direction: $_direction');
634 635
    description.add('mainAxisAlignment: $_mainAxisAlignment');
    description.add('crossAxisAlignment: $_crossAxisAlignment');
636
    description.add('textBaseline: $_textBaseline');
637
  }
638

639
}