flex.dart 23.4 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 49 50
  spaceAround,
}

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

typedef double _ChildSizingFunction(RenderBox child, BoxConstraints constraints);

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
/// 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.
82 83
class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, FlexParentData>,
                                        RenderBoxContainerDefaultsMixin<RenderBox, FlexParentData> {
84 85

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

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

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

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

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

138
  /// Set during layout if overflow occurred on the main axis
139
  double _overflow;
140

141
  void setupParentData(RenderBox child) {
142 143
    if (child.parentData is! FlexParentData)
      child.parentData = new FlexParentData();
144 145 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
  }

  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
177 178
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
179 180 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
      }
      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
240 241
        final FlexParentData childParentData = child.parentData;
        child = childParentData.nextSibling;
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
      }

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

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

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

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

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

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

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

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

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

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

  void hitTestChildren(HitTestResult result, { Point position }) {
    defaultHitTestChildren(result, position: position);
  }

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

552 553 554 555 556 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
    // We have overflow. Clip it.
    context.canvas.save();
    context.canvas.clipRect(offset & size);
    defaultPaint(context, offset);
    context.canvas.restore();
    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

Hixie's avatar
Hixie committed
597
  String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}direction: $_direction\n${prefix}justifyContent: $_justifyContent\n${prefix}alignItems: $_alignItems\n${prefix}textBaseline: $_textBaseline\n';
598

599
}