flex.dart 23.6 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

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
  void merge(FlexParentData other) {
21 22 23 24 25 26 27 28
    if (other.flex != null)
      flex = other.flex;
    super.merge(other);
  }

  String toString() => '${super.toString()}; flex=$flex';
}

29 30 31 32 33 34 35
/// 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
}
36

37
/// How the children should be placed along the main axis in a flex layout
38
enum FlexJustifyContent {
39
  /// Place the children as close to the start of the main axis as possible
40
  start,
41
  /// Place the children as close to the end of the main axis as possible
42
  end,
43
  /// Place the children as close to the middle of the main axis as possible
44
  center,
45
  /// Place the free space evenly between the children
46
  spaceBetween,
47
  /// Place the free space evenly between the children as well as before and after the first and last child
48
  spaceAround,
49 50
  /// Do not expand to fill the free space. None of the children may specify a flex factor.
  collapse,
51 52
}

53
/// How the children should be placed along the cross axis in a flex layout
54
enum FlexAlignItems {
55
  /// Place the children as close to the start of the cross axis as possible
56
  start,
57
  /// Place the children as close to the end of the cross axis as possible
58
  end,
59
  /// Place the children as close to the middle of the cross axis as possible
60
  center,
61
  /// Require the children to fill the cross axis
62
  stretch,
63
  /// Place the children along the cross axis such that their baselines match
64
  baseline,
65 66 67 68
}

typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/// 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
/// flexible children) is allocated according to the [justifyContent] property.
///
/// 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
/// positioned along the cross axis according to the [alignItems] property.
84 85
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
                                        RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData> {
86 87

  RenderFlex({
88
    List<RenderBox> children,
89 90
    FlexDirection direction: FlexDirection.horizontal,
    FlexJustifyContent justifyContent: FlexJustifyContent.start,
91 92
    FlexAlignItems alignItems: FlexAlignItems.center,
    TextBaseline textBaseline
93 94
  }) : _direction = direction,
       _justifyContent = justifyContent,
95 96
       _alignItems = alignItems,
       _textBaseline = textBaseline {
97 98 99
    addAll(children);
  }

100
  /// The direction to use as the main axis
101
  FlexDirection get direction => _direction;
102
  FlexDirection _direction;
103 104 105 106 107 108 109
  void set direction (FlexDirection value) {
    if (_direction != value) {
      _direction = value;
      markNeedsLayout();
    }
  }

110
  /// How the children should be placed along the main axis
111
  FlexJustifyContent get justifyContent => _justifyContent;
112
  FlexJustifyContent _justifyContent;
113 114 115 116 117 118 119
  void set justifyContent (FlexJustifyContent value) {
    if (_justifyContent != value) {
      _justifyContent = value;
      markNeedsLayout();
    }
  }

120
  /// How the children should be placed along the cross axis
121
  FlexAlignItems get alignItems => _alignItems;
122
  FlexAlignItems _alignItems;
123 124 125 126 127 128 129
  void set alignItems (FlexAlignItems value) {
    if (_alignItems != value) {
      _alignItems = value;
      markNeedsLayout();
    }
  }

130
  /// If using aligning items according to their baseline, which baseline to use
131
  TextBaseline get textBaseline => _textBaseline;
132
  TextBaseline _textBaseline;
133 134 135 136 137 138 139
  void set textBaseline (TextBaseline value) {
    if (_textBaseline != value) {
      _textBaseline = value;
      markNeedsLayout();
    }
  }

140
  /// Set during layout if overflow occurred on the main axis
141
  double _overflow;
142

143
  void setupParentData(RenderBox child) {
144 145
    if (child.parentData is! FlexParentData)
      child.parentData = new FlexParentData();
146 147 148 149 150
  }

  double _getIntrinsicSize({ BoxConstraints constraints,
                             FlexDirection sizingDirection,
                             _ChildSizingFunction childSize }) {
151
    assert(constraints.isNormalized);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    // http://www.w3.org/TR/2015/WD-css-flexbox-1-20150514/#intrinsic-sizes
    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.
      BoxConstraints childConstraints;
      switch(_direction) {
        case FlexDirection.horizontal:
          childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
          break;
        case FlexDirection.vertical:
          childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
          break;
      }

      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) {
          double flexFraction = childSize(child, childConstraints) / _getFlex(child);
          maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction);
        } else {
          inflexibleSpace += childSize(child, childConstraints);
        }
Hixie's avatar
Hixie committed
180 181
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
      }
      double mainSize = maxFlexFractionSoFar * totalFlex + inflexibleSpace;

      // Ensure that we don't violate the given constraints with our result
      switch(_direction) {
        case FlexDirection.horizontal:
          return constraints.constrainWidth(mainSize);
        case FlexDirection.vertical:
          return constraints.constrainHeight(mainSize);
      }
    } else {
      // INTRINSIC CROSS SIZE
      // The spec wants us to perform layout into the given available main-axis
      // space and return the cross size. That's too expensive, so instead we
      // size inflexible children according to their max intrinsic size in the
      // main direction and use those constraints to determine their max
      // intrinsic size in the cross direction. We don't care if the caller
      // asked for max or min -- the answer is always computed using the
      // max size in the main direction.

      double availableMainSpace;
      BoxConstraints childConstraints;
      switch(_direction) {
        case FlexDirection.horizontal:
          childConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
          availableMainSpace = constraints.maxWidth;
          break;
        case FlexDirection.vertical:
          childConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
          availableMainSpace = constraints.maxHeight;
          break;
      }

      // Get inflexible space using the max in the main direction
      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:
                mainSize = child.getMaxIntrinsicWidth(childConstraints);
                BoxConstraints widthConstraints =
                  new BoxConstraints(minWidth: mainSize, maxWidth: mainSize);
                crossSize = child.getMaxIntrinsicHeight(widthConstraints);
                break;
              case FlexDirection.vertical:
                mainSize = child.getMaxIntrinsicHeight(childConstraints);
                BoxConstraints heightConstraints =
                  new BoxConstraints(minWidth: mainSize, maxWidth: mainSize);
                crossSize = child.getMaxIntrinsicWidth(heightConstraints);
                break;
          }
          inflexibleSpace += mainSize;
          maxCrossSize = math.max(maxCrossSize, crossSize);
        }
Hixie's avatar
Hixie committed
243 244
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
      }

      // Determine the spacePerFlex by allocating the remaining available space
      double spacePerFlex = (availableMainSpace - inflexibleSpace) / totalFlex;

      // Size remaining items, find the maximum cross size
      child = firstChild;
      while (child != null) {
        int flex = _getFlex(child);
        if (flex > 0) {
          double childMainSize = spacePerFlex * flex;
          double crossSize;
          switch (_direction) {
            case FlexDirection.horizontal:
              BoxConstraints childConstraints =
                new BoxConstraints(minWidth: childMainSize, maxWidth: childMainSize);
              crossSize = child.getMaxIntrinsicHeight(childConstraints);
              break;
            case FlexDirection.vertical:
              BoxConstraints childConstraints =
                new BoxConstraints(minHeight: childMainSize, maxHeight: childMainSize);
              crossSize = child.getMaxIntrinsicWidth(childConstraints);
              break;
          }
          maxCrossSize = math.max(maxCrossSize, crossSize);
        }
Hixie's avatar
Hixie committed
271 272
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
      }

      // Ensure that we don't violate the given constraints with our result
      switch(_direction) {
        case FlexDirection.horizontal:
          return constraints.constrainHeight(maxCrossSize);
        case FlexDirection.vertical:
          return constraints.constrainWidth(maxCrossSize);
      }
    }
  }

  double getMinIntrinsicWidth(BoxConstraints constraints) {
    return _getIntrinsicSize(
      constraints: constraints,
      sizingDirection: FlexDirection.horizontal,
Hixie's avatar
Hixie committed
289
      childSize: (RenderBox child, BoxConstraints innerConstraints) => child.getMinIntrinsicWidth(innerConstraints)
290 291 292 293 294 295 296
    );
  }

  double getMaxIntrinsicWidth(BoxConstraints constraints) {
    return _getIntrinsicSize(
      constraints: constraints,
      sizingDirection: FlexDirection.horizontal,
Hixie's avatar
Hixie committed
297
      childSize: (RenderBox child, BoxConstraints innerConstraints) => child.getMaxIntrinsicWidth(innerConstraints)
298 299 300 301 302 303 304
    );
  }

  double getMinIntrinsicHeight(BoxConstraints constraints) {
    return _getIntrinsicSize(
      constraints: constraints,
      sizingDirection: FlexDirection.vertical,
Hixie's avatar
Hixie committed
305
      childSize: (RenderBox child, BoxConstraints innerConstraints) => child.getMinIntrinsicHeight(innerConstraints)
306 307 308 309 310 311 312
    );
  }

  double getMaxIntrinsicHeight(BoxConstraints constraints) {
    return _getIntrinsicSize(
      constraints: constraints,
      sizingDirection: FlexDirection.vertical,
Hixie's avatar
Hixie committed
313
      childSize: (RenderBox child, BoxConstraints innerConstraints) => child.getMaxIntrinsicHeight(innerConstraints));
314 315 316 317 318 319 320 321 322
  }

  double computeDistanceToActualBaseline(TextBaseline baseline) {
    if (_direction == FlexDirection.horizontal)
      return defaultComputeDistanceToHighestActualBaseline(baseline);
    return defaultComputeDistanceToFirstActualBaseline(baseline);
  }

  int _getFlex(RenderBox child) {
Hixie's avatar
Hixie committed
323 324
    final FlexParentData childParentData = child.parentData;
    return childParentData.flex != null ? childParentData.flex : 0;
325 326 327 328 329 330 331 332 333 334 335
  }

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

  void performLayout() {
336 337 338
    // Originally based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths

    // Determine used flex factor, size inflexible items, calculate free space.
339 340 341
    int totalFlex = 0;
    int totalChildren = 0;
    assert(constraints != null);
342
    final double mainSize = (_direction == FlexDirection.horizontal) ? constraints.constrainWidth() : constraints.constrainHeight();
343
    final bool canFlex = mainSize < double.INFINITY && justifyContent != FlexJustifyContent.collapse;
344 345
    double crossSize = 0.0;  // This is determined as we lay out the children
    double freeSpace = canFlex ? mainSize : 0.0;
346 347
    RenderBox child = firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
348
      final FlexParentData childParentData = child.parentData;
349 350 351
      totalChildren++;
      int flex = _getFlex(child);
      if (flex > 0) {
352
        // Flexible children can only be used when the RenderFlex box's container has a finite size.
Hans Muller's avatar
Hans Muller committed
353
        // When the container is infinite, for example if you are in a scrollable viewport, then
354
        // it wouldn't make any sense to have a flexible child.
355
        assert(canFlex && 'See https://flutter.github.io/layout/#flex' is String);
Hixie's avatar
Hixie committed
356
        totalFlex += childParentData.flex;
357 358 359 360 361
      } else {
        BoxConstraints innerConstraints;
        if (alignItems == FlexAlignItems.stretch) {
          switch (_direction) {
            case FlexDirection.horizontal:
362
              innerConstraints = new BoxConstraints(minHeight: constraints.maxHeight,
363 364 365
                                                    maxHeight: constraints.maxHeight);
              break;
            case FlexDirection.vertical:
366
              innerConstraints = new BoxConstraints(minWidth: constraints.maxWidth,
367
                                                    maxWidth: constraints.maxWidth);
368 369 370
              break;
          }
        } else {
371 372 373 374 375 376 377 378
          switch (_direction) {
            case FlexDirection.horizontal:
              innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight);
              break;
            case FlexDirection.vertical:
              innerConstraints = new BoxConstraints(maxWidth: constraints.maxWidth);
              break;
          }
379 380 381 382 383
        }
        child.layout(innerConstraints, parentUsesSize: true);
        freeSpace -= _getMainSize(child);
        crossSize = math.max(crossSize, _getCrossSize(child));
      }
Hixie's avatar
Hixie committed
384 385
      assert(child.parentData == childParentData);
      child = childParentData.nextSibling;
386
    }
387 388
    _overflow = math.max(0.0, -freeSpace);
    freeSpace = math.max(0.0, freeSpace);
389

390
    // Distribute remaining space to flexible children, and determine baseline.
391
    double maxBaselineDistance = 0.0;
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    double usedSpace = 0.0;
    if (totalFlex > 0 || alignItems == FlexAlignItems.baseline) {
      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;
          if (alignItems == FlexAlignItems.stretch) {
            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;
            }
429
          }
430 431 432
          child.layout(innerConstraints, parentUsesSize: true);
          usedSpace += _getMainSize(child);
          crossSize = math.max(crossSize, _getCrossSize(child));
433
        }
434
        if (alignItems == FlexAlignItems.baseline) {
435
          assert(textBaseline != null && 'To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.' is String);
436 437 438 439
          double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
          if (distance != null)
            maxBaselineDistance = math.max(maxBaselineDistance, distance);
        }
Hixie's avatar
Hixie committed
440 441
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
442
      }
443 444
    }

445
    // Align items along the main axis.
446 447
    double leadingSpace;
    double betweenSpace;
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    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:
468
          size = constraints.constrain(new Size(_overflow, crossSize));
469
          crossSize = size.height;
470
          remainingSpace = math.max(0.0, size.width - _overflow);
471 472
          break;
        case FlexDirection.vertical:
473
          size = constraints.constrain(new Size(crossSize, _overflow));
474
          crossSize = size.width;
475
          remainingSpace = math.max(0.0, size.height - _overflow);
476 477 478 479
          break;
      }
      _overflow = 0.0;
    }
480 481
    switch (_justifyContent) {
      case FlexJustifyContent.start:
482
      case FlexJustifyContent.collapse:
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
        leadingSpace = 0.0;
        betweenSpace = 0.0;
        break;
      case FlexJustifyContent.end:
        leadingSpace = remainingSpace;
        betweenSpace = 0.0;
        break;
      case FlexJustifyContent.center:
        leadingSpace = remainingSpace / 2.0;
        betweenSpace = 0.0;
        break;
      case FlexJustifyContent.spaceBetween:
        leadingSpace = 0.0;
        betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0;
        break;
      case FlexJustifyContent.spaceAround:
        betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0;
        leadingSpace = betweenSpace / 2.0;
        break;
    }

Collin Jackson's avatar
Collin Jackson committed
504
    // Position elements
505 506 507
    double childMainPosition = leadingSpace;
    child = firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
508
      final FlexParentData childParentData = child.parentData;
509 510 511 512 513 514 515 516 517 518 519 520
      double childCrossPosition;
      switch (_alignItems) {
        case FlexAlignItems.stretch:
        case FlexAlignItems.start:
          childCrossPosition = 0.0;
          break;
        case FlexAlignItems.end:
          childCrossPosition = crossSize - _getCrossSize(child);
          break;
        case FlexAlignItems.center:
          childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0;
          break;
521 522 523
        case FlexAlignItems.baseline:
          childCrossPosition = 0.0;
          if (_direction == FlexDirection.horizontal) {
524 525
            assert(textBaseline != null);
            double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
526 527 528 529
            if (distance != null)
              childCrossPosition = maxBaselineDistance - distance;
          }
          break;
530 531 532
      }
      switch (_direction) {
        case FlexDirection.horizontal:
Hixie's avatar
Hixie committed
533
          childParentData.position = new Point(childMainPosition, childCrossPosition);
534 535
          break;
        case FlexDirection.vertical:
Hixie's avatar
Hixie committed
536
          childParentData.position = new Point(childCrossPosition, childMainPosition);
537 538 539
          break;
      }
      childMainPosition += _getMainSize(child) + betweenSpace;
Hixie's avatar
Hixie committed
540
      child = childParentData.nextSibling;
541 542 543
    }
  }

Adam Barth's avatar
Adam Barth committed
544 545
  bool hitTestChildren(HitTestResult result, { Point position }) {
    return defaultHitTestChildren(result, position: position);
546 547
  }

548
  void paint(PaintingContext context, Offset offset) {
549
    if (_overflow <= 0.0) {
550
      defaultPaint(context, offset);
551
      return;
552
    }
Collin Jackson's avatar
Collin Jackson committed
553

554
    // We have overflow. Clip it.
555 556
    context.pushClipRect(needsCompositing, offset, Point.origin & size, defaultPaint);

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
    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);
      const kMarkerSize = 0.1;
      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
589

590 591
  String toString() {
    String header = super.toString();
592
    if (_overflow is double && _overflow > 0.0)
593 594
      header += ' OVERFLOWING';
    return header;
Collin Jackson's avatar
Collin Jackson committed
595
  }
596

597 598 599 600 601 602 603
  void debugDescribeSettings(List<String> settings) {
    super.debugDescribeSettings(settings);
    settings.add('direction: $_direction');
    settings.add('justifyContent: $_justifyContent');
    settings.add('alignItems: $_alignItems');
    settings.add('textBaseline: $_textBaseline');
  }
604

605
}