flex_test.dart 28.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Ian Hickson's avatar
Ian Hickson committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/foundation.dart';
6
import 'package:flutter/rendering.dart';
7
import 'package:flutter_test/flutter_test.dart';
8 9 10 11

import 'rendering_tester.dart';

void main() {
12 13
  TestRenderingFlutterBinding.ensureInitialized();

14
  test('Overconstrained flex', () {
15 16
    final RenderDecoratedBox box = RenderDecoratedBox(decoration: const BoxDecoration());
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr, children: <RenderBox>[box]);
17
    layout(flex, constraints: const BoxConstraints(
18 19 20 21 22
      minWidth: 200.0,
      maxWidth: 200.0,
      minHeight: 200.0,
      maxHeight: 200.0,
    ));
23

24 25
    expect(flex.size.width, equals(200.0), reason: 'flex width');
    expect(flex.size.height, equals(200.0), reason: 'flex height');
26
  });
27

28 29 30 31 32 33
  test('Inconsequential overflow is ignored', () {
    // These values are meant to simulate slight rounding errors in addition
    // or subtraction in the layout code for Flex.
    const double slightlyLarger = 438.8571428571429;
    const double slightlySmaller = 438.85714285714283;
    final List<dynamic> exceptions = <dynamic>[];
34
    final FlutterExceptionHandler? oldHandler = FlutterError.onError;
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    FlutterError.onError = (FlutterErrorDetails details) {
      exceptions.add(details.exception);
    };
    const BoxConstraints square = BoxConstraints.tightFor(width: slightlyLarger, height: 100.0);
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(
      textDirection: TextDirection.ltr,
      mainAxisSize: MainAxisSize.min,
    );
    final RenderConstrainedOverflowBox parent = RenderConstrainedOverflowBox(
      minWidth: 0.0,
      maxWidth: slightlySmaller,
      minHeight: 0.0,
      maxHeight: 400.0,
      child: flex,
    );
    flex.add(box1);
    layout(parent);
    expect(flex.size, const Size(slightlySmaller, 100.0));
    pumpFrame(phase: EnginePhase.paint);

    expect(exceptions, isEmpty);
    FlutterError.onError = oldHandler;
  });

60 61 62
  test('Clip behavior is respected', () {
    const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
    final TestClipPaintingContext context = TestClipPaintingContext();
63
    bool hadErrors = false;
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    for (final Clip? clip in <Clip?>[null, ...Clip.values]) {
      final RenderFlex flex;
      switch (clip) {
        case Clip.none:
        case Clip.hardEdge:
        case Clip.antiAlias:
        case Clip.antiAliasWithSaveLayer:
          flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200], clipBehavior: clip!);
          break;
        case null:
          flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200]);
          break;
      }
      layout(flex, constraints: viewport, phase: EnginePhase.composite, onErrors: () {
        absorbOverflowedErrors();
        hadErrors = true;
      });
82
      context.paintChild(flex, Offset.zero);
83 84 85 86
      // By default, clipBehavior should be Clip.none
      expect(context.clipBehavior, equals(clip ?? Clip.none));
      expect(hadErrors, isTrue);
      hadErrors = false;
87 88 89
    }
  });

90
  test('Vertical Overflow', () {
91
    final RenderConstrainedBox flexible = RenderConstrainedBox(
92
      additionalConstraints: const BoxConstraints.expand(),
93
    );
94
    final RenderFlex flex = RenderFlex(
95
      direction: Axis.vertical,
96
      children: <RenderBox>[
97
        RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0)),
98
        flexible,
99
      ],
100
    );
101
    final FlexParentData flexParentData = flexible.parentData! as FlexParentData;
102
    flexParentData.flex = 1;
103
    const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
104 105
    layout(flex, constraints: viewport);
    expect(flexible.size.height, equals(0.0));
106 107 108 109
    expect(flex.getMinIntrinsicHeight(100.0), equals(200.0));
    expect(flex.getMaxIntrinsicHeight(100.0), equals(200.0));
    expect(flex.getMinIntrinsicWidth(100.0), equals(0.0));
    expect(flex.getMaxIntrinsicWidth(100.0), equals(0.0));
110 111 112
  });

  test('Horizontal Overflow', () {
113
    final RenderConstrainedBox flexible = RenderConstrainedBox(
114
      additionalConstraints: const BoxConstraints.expand(),
115
    );
116
    final RenderFlex flex = RenderFlex(
117
      textDirection: TextDirection.ltr,
118
      children: <RenderBox>[
119
        RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 200.0)),
120
        flexible,
121
      ],
122
    );
123
    final FlexParentData flexParentData = flexible.parentData! as FlexParentData;
124
    flexParentData.flex = 1;
125
    const BoxConstraints viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
126 127
    layout(flex, constraints: viewport);
    expect(flexible.size.width, equals(0.0));
128 129 130 131
    expect(flex.getMinIntrinsicHeight(100.0), equals(0.0));
    expect(flex.getMaxIntrinsicHeight(100.0), equals(0.0));
    expect(flex.getMinIntrinsicWidth(100.0), equals(200.0));
    expect(flex.getMaxIntrinsicWidth(100.0), equals(200.0));
132 133 134
  });

  test('Vertical Flipped Constraints', () {
135
    final RenderFlex flex = RenderFlex(
136
      direction: Axis.vertical,
137
      children: <RenderBox>[
138
        RenderAspectRatio(aspectRatio: 1.0),
139
      ],
140
    );
141
    const BoxConstraints viewport = BoxConstraints(maxHeight: 200.0, maxWidth: 1000.0);
142
    layout(flex, constraints: viewport);
143
    expect(flex.getMaxIntrinsicWidth(200.0), equals(0.0));
144 145
  });

146
  // We can't write a horizontal version of the above test due to
147 148
  // RenderAspectRatio being height-in, width-out.

149
  test('Defaults', () {
150
    final RenderFlex flex = RenderFlex();
151
    expect(flex.crossAxisAlignment, equals(CrossAxisAlignment.center));
152
    expect(flex.direction, equals(Axis.horizontal));
153 154
    expect(flex, hasAGoodToStringDeep);
    expect(
155
      flex.toStringDeep(minLevel: DiagnosticLevel.info),
156 157
      equalsIgnoringHashCodes(
        'RenderFlex#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
Ian Hickson's avatar
Ian Hickson committed
158 159
        '   parentData: MISSING\n'
        '   constraints: MISSING\n'
160 161 162 163 164
        '   size: MISSING\n'
        '   direction: horizontal\n'
        '   mainAxisAlignment: start\n'
        '   mainAxisSize: max\n'
        '   crossAxisAlignment: center\n'
165
        '   verticalDirection: down\n',
166 167
      ),
    );
168 169 170
  });

  test('Parent data', () {
171 172 173
    final RenderDecoratedBox box1 = RenderDecoratedBox(decoration: const BoxDecoration());
    final RenderDecoratedBox box2 = RenderDecoratedBox(decoration: const BoxDecoration());
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr, children: <RenderBox>[box1, box2]);
174
    layout(flex, constraints: const BoxConstraints(
175 176 177
      maxWidth: 100.0,
      maxHeight: 100.0,
    ));
178 179 180 181 182
    expect(box1.size.width, equals(0.0));
    expect(box1.size.height, equals(0.0));
    expect(box2.size.width, equals(0.0));
    expect(box2.size.height, equals(0.0));

183
    final FlexParentData box2ParentData = box2.parentData! as FlexParentData;
184
    box2ParentData.flex = 1;
185 186 187 188 189 190 191 192 193
    flex.markNeedsLayout();
    pumpFrame();
    expect(box1.size.width, equals(0.0));
    expect(box1.size.height, equals(0.0));
    expect(box2.size.width, equals(100.0));
    expect(box2.size.height, equals(0.0));
  });

  test('Stretch', () {
194 195 196
    final RenderDecoratedBox box1 = RenderDecoratedBox(decoration: const BoxDecoration());
    final RenderDecoratedBox box2 = RenderDecoratedBox(decoration: const BoxDecoration());
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr);
197
    flex.setupParentData(box2);
198
    final FlexParentData box2ParentData = box2.parentData! as FlexParentData;
199 200
    box2ParentData.flex = 2;
    flex.addAll(<RenderBox>[box1, box2]);
201
    layout(flex, constraints: const BoxConstraints(
202 203 204
      maxWidth: 100.0,
      maxHeight: 100.0,
    ));
205 206 207 208 209
    expect(box1.size.width, equals(0.0));
    expect(box1.size.height, equals(0.0));
    expect(box2.size.width, equals(100.0));
    expect(box2.size.height, equals(0.0));

210
    flex.crossAxisAlignment = CrossAxisAlignment.stretch;
211 212 213 214 215 216
    pumpFrame();
    expect(box1.size.width, equals(0.0));
    expect(box1.size.height, equals(100.0));
    expect(box2.size.width, equals(100.0));
    expect(box2.size.height, equals(100.0));

217
    flex.direction = Axis.vertical;
218 219 220 221 222 223
    pumpFrame();
    expect(box1.size.width, equals(100.0));
    expect(box1.size.height, equals(0.0));
    expect(box2.size.width, equals(100.0));
    expect(box2.size.height, equals(100.0));
  });
224 225

  test('Space evenly', () {
226 227 228 229
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr, mainAxisAlignment: MainAxisAlignment.spaceEvenly);
230 231
    flex.addAll(<RenderBox>[box1, box2, box3]);
    layout(flex, constraints: const BoxConstraints(
232 233 234
      maxWidth: 500.0,
      maxHeight: 400.0,
    ));
235
    Offset getOffset(RenderBox box) {
236
      final FlexParentData parentData = box.parentData! as FlexParentData;
237 238 239 240 241 242 243 244 245
      return parentData.offset;
    }
    expect(getOffset(box1).dx, equals(50.0));
    expect(box1.size.width, equals(100.0));
    expect(getOffset(box2).dx, equals(200.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(350.0));
    expect(box3.size.width, equals(100.0));

246
    flex.direction = Axis.vertical;
247 248 249 250 251 252 253 254
    pumpFrame();
    expect(getOffset(box1).dy, equals(25.0));
    expect(box1.size.height, equals(100.0));
    expect(getOffset(box2).dy, equals(150.0));
    expect(box2.size.height, equals(100.0));
    expect(getOffset(box3).dy, equals(275.0));
    expect(box3.size.height, equals(100.0));
  });
Adam Barth's avatar
Adam Barth committed
255 256

  test('Fit.loose', () {
257 258 259 260
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr, mainAxisAlignment: MainAxisAlignment.spaceBetween);
Adam Barth's avatar
Adam Barth committed
261 262
    flex.addAll(<RenderBox>[box1, box2, box3]);
    layout(flex, constraints: const BoxConstraints(
263 264 265
      maxWidth: 500.0,
      maxHeight: 400.0,
    ));
Adam Barth's avatar
Adam Barth committed
266
    Offset getOffset(RenderBox box) {
267
      final FlexParentData parentData = box.parentData! as FlexParentData;
Adam Barth's avatar
Adam Barth committed
268 269 270 271 272 273 274 275 276 277
      return parentData.offset;
    }
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(100.0));
    expect(getOffset(box2).dx, equals(200.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(400.0));
    expect(box3.size.width, equals(100.0));

    void setFit(RenderBox box, FlexFit fit) {
278
      final FlexParentData parentData = box.parentData! as FlexParentData;
Adam Barth's avatar
Adam Barth committed
279 280 281 282 283
      parentData.flex = 1;
      parentData.fit = fit;
    }

    setFit(box1, FlexFit.loose);
284
    flex.markNeedsLayout();
Adam Barth's avatar
Adam Barth committed
285 286 287 288 289 290 291 292 293

    pumpFrame();
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(100.0));
    expect(getOffset(box2).dx, equals(200.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(400.0));
    expect(box3.size.width, equals(100.0));

294
    box1.additionalConstraints = const BoxConstraints.tightFor(width: 1000.0, height: 100.0);
Adam Barth's avatar
Adam Barth committed
295 296 297 298 299 300 301 302 303

    pumpFrame();
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(300.0));
    expect(getOffset(box2).dx, equals(300.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(400.0));
    expect(box3.size.width, equals(100.0));
  });
304 305

  test('Flexible with MainAxisSize.min', () {
306 307 308 309
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0));
    final RenderFlex flex = RenderFlex(
310
      textDirection: TextDirection.ltr,
311
      mainAxisSize: MainAxisSize.min,
312
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
313 314 315
    );
    flex.addAll(<RenderBox>[box1, box2, box3]);
    layout(flex, constraints: const BoxConstraints(
316 317 318
      maxWidth: 500.0,
      maxHeight: 400.0,
    ));
319
    Offset getOffset(RenderBox box) {
320
      final FlexParentData parentData = box.parentData! as FlexParentData;
321 322 323 324 325 326 327 328 329 330 331
      return parentData.offset;
    }
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(100.0));
    expect(getOffset(box2).dx, equals(100.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(200.0));
    expect(box3.size.width, equals(100.0));
    expect(flex.size.width, equals(300.0));

    void setFit(RenderBox box, FlexFit fit) {
332
      final FlexParentData parentData = box.parentData! as FlexParentData;
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
      parentData.flex = 1;
      parentData.fit = fit;
    }

    setFit(box1, FlexFit.tight);
    flex.markNeedsLayout();

    pumpFrame();
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(300.0));
    expect(getOffset(box2).dx, equals(300.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(400.0));
    expect(box3.size.width, equals(100.0));
    expect(flex.size.width, equals(500.0));

    setFit(box1, FlexFit.loose);
    flex.markNeedsLayout();

    pumpFrame();
    expect(getOffset(box1).dx, equals(0.0));
    expect(box1.size.width, equals(100.0));
    expect(getOffset(box2).dx, equals(100.0));
    expect(box2.size.width, equals(100.0));
    expect(getOffset(box3).dx, equals(200.0));
    expect(box3.size.width, equals(100.0));
    expect(flex.size.width, equals(300.0));
  });
361 362 363

  test('MainAxisSize.min inside unconstrained', () {
    FlutterError.onError = (FlutterErrorDetails details) => throw details.exception;
364
    const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
365 366 367 368
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(
369
      textDirection: TextDirection.ltr,
370 371
      mainAxisSize: MainAxisSize.min,
    );
372
    final RenderConstrainedOverflowBox parent = RenderConstrainedOverflowBox(
373
      minWidth: 0.0,
374
      maxWidth: double.infinity,
375 376 377 378 379 380 381
      minHeight: 0.0,
      maxHeight: 400.0,
      child: flex,
    );
    flex.addAll(<RenderBox>[box1, box2, box3]);
    layout(parent);
    expect(flex.size, const Size(300.0, 100.0));
382
    final FlexParentData box2ParentData = box2.parentData! as FlexParentData;
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
    box2ParentData.flex = 1;
    box2ParentData.fit = FlexFit.loose;
    flex.markNeedsLayout();
    pumpFrame();
    expect(flex.size, const Size(300.0, 100.0));
    parent.maxWidth = 500.0; // NOW WITH CONSTRAINED BOUNDARIES
    pumpFrame();
    expect(flex.size, const Size(300.0, 100.0));
    flex.mainAxisSize = MainAxisSize.max;
    pumpFrame();
    expect(flex.size, const Size(500.0, 100.0));
    flex.mainAxisSize = MainAxisSize.min;
    box2ParentData.fit = FlexFit.tight;
    flex.markNeedsLayout();
    pumpFrame();
    expect(flex.size, const Size(500.0, 100.0));
    parent.maxWidth = 505.0;
    pumpFrame();
    expect(flex.size, const Size(505.0, 100.0));
  });

  test('MainAxisSize.min inside unconstrained', () {
405
    const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
406 407 408 409
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(
410
      textDirection: TextDirection.ltr,
411 412
      mainAxisSize: MainAxisSize.min,
    );
413
    final RenderConstrainedOverflowBox parent = RenderConstrainedOverflowBox(
414
      minWidth: 0.0,
415
      maxWidth: double.infinity,
416 417 418 419 420
      minHeight: 0.0,
      maxHeight: 400.0,
      child: flex,
    );
    flex.addAll(<RenderBox>[box1, box2, box3]);
421
    final FlexParentData box2ParentData = box2.parentData! as FlexParentData;
422
    box2ParentData.flex = 1;
423 424
    final List<dynamic> exceptions = <dynamic>[];
    layout(parent, onErrors: () {
425
      exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
426
    });
427
    expect(exceptions, isNotEmpty);
Dan Field's avatar
Dan Field committed
428
    expect(exceptions.first, isFlutterError);
429 430 431
  });

  test('MainAxisSize.min inside unconstrained', () {
432
    const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
433 434 435 436
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(
437
      textDirection: TextDirection.ltr,
438
    );
439
    final RenderConstrainedOverflowBox parent = RenderConstrainedOverflowBox(
440
      minWidth: 0.0,
441
      maxWidth: double.infinity,
442 443 444 445 446
      minHeight: 0.0,
      maxHeight: 400.0,
      child: flex,
    );
    flex.addAll(<RenderBox>[box1, box2, box3]);
447
    final FlexParentData box2ParentData = box2.parentData! as FlexParentData;
448 449
    box2ParentData.flex = 1;
    box2ParentData.fit = FlexFit.loose;
450 451
    final List<dynamic> exceptions = <dynamic>[];
    layout(parent, onErrors: () {
452
      exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
453
    });
454
    expect(exceptions, isNotEmpty);
Dan Field's avatar
Dan Field committed
455
    expect(exceptions.first, isFlutterError);
456
  });
457

458
  test('MainAxisSize.min inside tightly constrained', () {
459
    const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
460 461 462 463
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(
464 465 466 467 468 469
      textDirection: TextDirection.rtl,
      mainAxisSize: MainAxisSize.min,
    );
    flex.addAll(<RenderBox>[box1, box2, box3]);
    layout(flex);
    expect(flex.constraints.hasTightWidth, isTrue);
470 471 472
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 250.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 250.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 250.0));
473 474 475 476 477
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));
  });

478
  test('Flex RTL', () {
479
    const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
480 481 482 483
    final RenderConstrainedBox box1 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box2 = RenderConstrainedBox(additionalConstraints: square);
    final RenderConstrainedBox box3 = RenderConstrainedBox(additionalConstraints: square);
    final RenderFlex flex = RenderFlex(textDirection: TextDirection.ltr, children: <RenderBox>[box1, box2, box3]);
484
    layout(flex);
485 486 487
    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 250.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(100.0, 250.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(200.0, 250.0));
488 489 490 491 492 493
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.mainAxisAlignment = MainAxisAlignment.end;
    pumpFrame();
494 495 496
    expect(box1.localToGlobal(Offset.zero), const Offset(500.0, 250.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 250.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(700.0, 250.0));
497 498 499 500 501 502
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.textDirection = TextDirection.rtl;
    pumpFrame();
503 504 505
    expect(box1.localToGlobal(Offset.zero), const Offset(200.0, 250.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(100.0, 250.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, 250.0));
506 507 508 509 510 511
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.mainAxisAlignment = MainAxisAlignment.start;
    pumpFrame();
512 513 514
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 250.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 250.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 250.0));
515 516 517 518 519 520
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.start; // vertical direction is down
    pumpFrame();
521 522 523
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 0.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 0.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 0.0));
524 525 526 527 528 529
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.end;
    pumpFrame();
530 531 532
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 500.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 500.0));
533 534 535 536 537 538
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.verticalDirection = VerticalDirection.up;
    pumpFrame();
539 540 541
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 0.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 0.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 0.0));
542 543 544 545 546 547
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.start;
    pumpFrame();
548 549 550
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(600.0, 500.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(500.0, 500.0));
551 552 553 554 555 556
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.direction = Axis.vertical; // and main=start, cross=start, up, rtl
    pumpFrame();
557 558 559
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(700.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(700.0, 300.0));
560 561 562 563 564 565
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.end;
    pumpFrame();
566 567 568
    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(0.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, 300.0));
569 570 571 572 573 574
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.stretch;
    pumpFrame();
575 576 577
    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(0.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, 300.0));
578 579 580 581 582 583
    expect(box1.size, const Size(800.0, 100.0));
    expect(box2.size, const Size(800.0, 100.0));
    expect(box3.size, const Size(800.0, 100.0));

    flex.textDirection = TextDirection.ltr;
    pumpFrame();
584 585 586
    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(0.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, 300.0));
587 588 589 590 591 592
    expect(box1.size, const Size(800.0, 100.0));
    expect(box2.size, const Size(800.0, 100.0));
    expect(box3.size, const Size(800.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.start;
    pumpFrame();
593 594 595
    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(0.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, 300.0));
596 597 598 599 600 601
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.crossAxisAlignment = CrossAxisAlignment.end;
    pumpFrame();
602 603 604
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 500.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(700.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(700.0, 300.0));
605 606 607 608 609 610
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.verticalDirection = VerticalDirection.down;
    pumpFrame();
611 612 613
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 0.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(700.0, 100.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(700.0, 200.0));
614 615 616 617 618 619
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));

    flex.mainAxisAlignment = MainAxisAlignment.end;
    pumpFrame();
620 621 622
    expect(box1.localToGlobal(Offset.zero), const Offset(700.0, 300.0));
    expect(box2.localToGlobal(Offset.zero), const Offset(700.0, 400.0));
    expect(box3.localToGlobal(Offset.zero), const Offset(700.0, 500.0));
623 624 625 626
    expect(box1.size, const Size(100.0, 100.0));
    expect(box2.size, const Size(100.0, 100.0));
    expect(box3.size, const Size(100.0, 100.0));
  });
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

  test('Intrinsics throw if alignment is baseline', () {
    final RenderDecoratedBox box = RenderDecoratedBox(
      decoration: const BoxDecoration(),
    );
    final RenderFlex flex = RenderFlex(
      textDirection: TextDirection.ltr,
      children: <RenderBox>[box],
      crossAxisAlignment: CrossAxisAlignment.baseline,
      textBaseline: TextBaseline.alphabetic,
    );
    layout(flex, constraints: const BoxConstraints(
      minWidth: 200.0, maxWidth: 200.0, minHeight: 200.0, maxHeight: 200.0,
    ));

    final Matcher cannotCalculateIntrinsics = throwsA(isAssertionError.having(
      (AssertionError e) => e.message,
      'message',
      'Intrinsics are not available for CrossAxisAlignment.baseline.',
    ));

    expect(() => flex.getMaxIntrinsicHeight(100), cannotCalculateIntrinsics);
    expect(() => flex.getMinIntrinsicHeight(100), cannotCalculateIntrinsics);
    expect(() => flex.getMaxIntrinsicWidth(100), cannotCalculateIntrinsics);
    expect(() => flex.getMinIntrinsicWidth(100), cannotCalculateIntrinsics);
  });
653 654 655 656 657 658 659

  test('Can call methods that check overflow even if overflow value is not set', () {
    final List<dynamic> exceptions = <dynamic>[];
    final RenderFlex flex = RenderFlex(children: const <RenderBox>[]);
    // This forces a check for _hasOverflow
    expect(flex.toStringShort(), isNot(contains('OVERFLOWING')));
    layout(flex, phase: EnginePhase.paint, onErrors: () {
660
      exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
661 662 663 664 665 666
    });
    // We expect the RenderFlex to throw during performLayout() for not having
    // a text direction, thus leaving it with a null overflow value. It'll then
    // try to paint(), which also checks _hasOverflow, and it should be able to
    // do so without an ancillary error.
    expect(exceptions, hasLength(1));
667
    // ignore: avoid_dynamic_calls
668 669
    expect(exceptions.first.message, isNot(contains('Null check operator')));
  });
670
}