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

  double _getIntrinsicSize({ BoxConstraints constraints,
                             FlexDirection sizingDirection,
                             _ChildSizingFunction childSize }) {
    // 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
179 180
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
181 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
      }
      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
242 243
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
244 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
      }

      // 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
270 271
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
      }

      // 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
288
      childSize: (RenderBox child, BoxConstraints innerConstraints) => child.getMinIntrinsicWidth(innerConstraints)
289 290 291 292 293 294 295
    );
  }

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

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

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

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

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

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

389
    // Distribute remaining space to flexible children, and determine baseline.
390
    double maxBaselineDistance = 0.0;
391 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
    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;
            }
428
          }
429 430 431
          child.layout(innerConstraints, parentUsesSize: true);
          usedSpace += _getMainSize(child);
          crossSize = math.max(crossSize, _getCrossSize(child));
432
        }
433
        if (alignItems == FlexAlignItems.baseline) {
434
          assert(textBaseline != null && 'To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.' is String);
435 436 437 438
          double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
          if (distance != null)
            maxBaselineDistance = math.max(maxBaselineDistance, distance);
        }
Hixie's avatar
Hixie committed
439 440
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
441
      }
442 443
    }

444
    // Align items along the main axis.
445 446
    double leadingSpace;
    double betweenSpace;
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    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:
467
          size = constraints.constrain(new Size(_overflow, crossSize));
468
          crossSize = size.height;
469 470
          assert(size.width >= _overflow);
          remainingSpace = size.width - _overflow;
471 472
          break;
        case FlexDirection.vertical:
473
          size = constraints.constrain(new Size(crossSize, _overflow));
474
          crossSize = size.width;
475 476
          assert(size.height >= _overflow);
          remainingSpace = size.height - _overflow;
477 478 479 480
          break;
      }
      _overflow = 0.0;
    }
481 482
    switch (_justifyContent) {
      case FlexJustifyContent.start:
483
      case FlexJustifyContent.collapse:
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
        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
505
    // Position elements
506 507 508
    double childMainPosition = leadingSpace;
    child = firstChild;
    while (child != null) {
Hixie's avatar
Hixie committed
509
      final FlexParentData childParentData = child.parentData;
510 511 512 513 514 515 516 517 518 519 520 521
      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;
522 523 524
        case FlexAlignItems.baseline:
          childCrossPosition = 0.0;
          if (_direction == FlexDirection.horizontal) {
525 526
            assert(textBaseline != null);
            double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true);
527 528 529 530
            if (distance != null)
              childCrossPosition = maxBaselineDistance - distance;
          }
          break;
531 532 533
      }
      switch (_direction) {
        case FlexDirection.horizontal:
Hixie's avatar
Hixie committed
534
          childParentData.position = new Point(childMainPosition, childCrossPosition);
535 536
          break;
        case FlexDirection.vertical:
Hixie's avatar
Hixie committed
537
          childParentData.position = new Point(childCrossPosition, childMainPosition);
538 539 540
          break;
      }
      childMainPosition += _getMainSize(child) + betweenSpace;
Hixie's avatar
Hixie committed
541
      child = childParentData.nextSibling;
542 543 544
    }
  }

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

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

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

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 589
    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
590

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

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

606
}