Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

1312 lines
52 KiB
Dart

// Copyright 2014 The Flutter 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;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'rendering_tester.dart';
void main() {
TestRenderingFlutterBinding.ensureInitialized();
test('Overconstrained flex', () {
final box = RenderDecoratedBox(decoration: const BoxDecoration());
final flex = RenderFlex(textDirection: TextDirection.ltr, children: <RenderBox>[box]);
layout(
flex,
constraints: const BoxConstraints(
minWidth: 200.0,
maxWidth: 200.0,
minHeight: 200.0,
maxHeight: 200.0,
),
);
expect(flex.size.width, equals(200.0), reason: 'flex width');
expect(flex.size.height, equals(200.0), reason: 'flex height');
});
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 slightlyLarger = 438.8571428571429;
const slightlySmaller = 438.85714285714283;
final exceptions = <dynamic>[];
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails details) {
exceptions.add(details.exception);
};
const square = BoxConstraints.tightFor(width: slightlyLarger, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.ltr, mainAxisSize: MainAxisSize.min);
final 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;
});
test('Clip behavior is respected', () {
const viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
final context = TestClipPaintingContext();
var hadErrors = false;
for (final 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!,
);
case null:
flex = RenderFlex(direction: Axis.vertical, children: <RenderBox>[box200x200]);
}
layout(
flex,
constraints: viewport,
phase: EnginePhase.composite,
onErrors: () {
absorbOverflowedErrors();
hadErrors = true;
},
);
context.paintChild(flex, Offset.zero);
// By default, clipBehavior should be Clip.none
expect(context.clipBehavior, equals(clip ?? Clip.none));
expect(hadErrors, isTrue);
hadErrors = false;
}
});
test('Vertical Overflow', () {
final flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand());
final flex = RenderFlex(
direction: Axis.vertical,
children: <RenderBox>[
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0)),
flexible,
],
);
final flexParentData = flexible.parentData! as FlexParentData;
flexParentData.flex = 1;
const viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(flex, constraints: viewport);
expect(flexible.size.height, equals(0.0));
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));
});
test('Vertical Overflow with RenderFlex.spacing', () {
final flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand());
final flex = RenderFlex(
direction: Axis.vertical,
spacing: 16.0,
children: <RenderBox>[
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0)),
flexible,
],
);
final flexParentData = flexible.parentData! as FlexParentData;
flexParentData.flex = 1;
const viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(flex, constraints: viewport);
expect(flexible.size.height, equals(0.0));
expect(flex.getMinIntrinsicHeight(100.0), equals(216.0));
expect(flex.getMaxIntrinsicHeight(100.0), equals(216.0));
expect(flex.getMinIntrinsicWidth(100.0), equals(0.0));
expect(flex.getMaxIntrinsicWidth(100.0), equals(0.0));
});
test('Horizontal Overflow', () {
final flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand());
final flex = RenderFlex(
textDirection: TextDirection.ltr,
children: <RenderBox>[
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 200.0)),
flexible,
],
);
final flexParentData = flexible.parentData! as FlexParentData;
flexParentData.flex = 1;
const viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(flex, constraints: viewport);
expect(flexible.size.width, equals(0.0));
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));
});
test('Horizontal Overflow with RenderFlex.spacing', () {
final flexible = RenderConstrainedBox(additionalConstraints: const BoxConstraints.expand());
final flex = RenderFlex(
textDirection: TextDirection.ltr,
spacing: 12.0,
children: <RenderBox>[
RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 200.0)),
flexible,
],
);
final flexParentData = flexible.parentData! as FlexParentData;
flexParentData.flex = 1;
const viewport = BoxConstraints(maxHeight: 100.0, maxWidth: 100.0);
layout(flex, constraints: viewport);
expect(flexible.size.width, equals(0.0));
expect(flex.getMinIntrinsicHeight(100.0), equals(0.0));
expect(flex.getMaxIntrinsicHeight(100.0), equals(0.0));
expect(flex.getMinIntrinsicWidth(100.0), equals(212.0));
expect(flex.getMaxIntrinsicWidth(100.0), equals(212.0));
});
test('Vertical Flipped Constraints', () {
final flex = RenderFlex(
direction: Axis.vertical,
children: <RenderBox>[RenderAspectRatio(aspectRatio: 1.0)],
);
const viewport = BoxConstraints(maxHeight: 200.0, maxWidth: 1000.0);
layout(flex, constraints: viewport);
expect(flex.getMaxIntrinsicWidth(200.0), equals(0.0));
});
// We can't write a horizontal version of the above test due to
// RenderAspectRatio being height-in, width-out.
test('Defaults', () {
final flex = RenderFlex();
expect(flex.crossAxisAlignment, equals(CrossAxisAlignment.center));
expect(flex.direction, equals(Axis.horizontal));
expect(flex, hasAGoodToStringDeep);
expect(
flex.toStringDeep(minLevel: DiagnosticLevel.info),
equalsIgnoringHashCodes(
'RenderFlex#00000 NEEDS-LAYOUT NEEDS-PAINT DETACHED\n'
' parentData: MISSING\n'
' constraints: MISSING\n'
' size: MISSING\n'
' direction: horizontal\n'
' mainAxisAlignment: start\n'
' mainAxisSize: max\n'
' crossAxisAlignment: center\n'
' verticalDirection: down\n'
' spacing: 0.0\n',
),
);
});
test('Parent data', () {
final box1 = RenderDecoratedBox(decoration: const BoxDecoration());
final box2 = RenderDecoratedBox(decoration: const BoxDecoration());
final flex = RenderFlex(textDirection: TextDirection.ltr, children: <RenderBox>[box1, box2]);
layout(flex, constraints: const BoxConstraints(maxWidth: 100.0, maxHeight: 100.0));
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));
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
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', () {
final box1 = RenderDecoratedBox(decoration: const BoxDecoration());
final box2 = RenderDecoratedBox(decoration: const BoxDecoration());
final flex = RenderFlex(textDirection: TextDirection.ltr);
flex.setupParentData(box2);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 2;
flex.addAll(<RenderBox>[box1, box2]);
layout(flex, constraints: const BoxConstraints(maxWidth: 100.0, maxHeight: 100.0));
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));
flex.crossAxisAlignment = CrossAxisAlignment.stretch;
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));
flex.direction = Axis.vertical;
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));
});
test('Space evenly', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
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));
flex.direction = Axis.vertical;
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));
});
test('MainAxisAlignment.start with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(textDirection: TextDirection.ltr, spacing: 14.0);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
return parentData.offset;
}
expect(getOffset(box1).dx, equals(0.0));
expect(box1.size.width, equals(100.0));
expect(getOffset(box2).dx, equals(114.0));
expect(box2.size.width, equals(100.0));
expect(getOffset(box3).dx, equals(228.0));
expect(box3.size.width, equals(100.0));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(0.0));
expect(box1.size.height, equals(100.0));
expect(getOffset(box2).dy, equals(114.0));
expect(box2.size.height, equals(100.0));
expect(getOffset(box3).dy, equals(228.0));
expect(box3.size.height, equals(100.0));
});
test('MainAxisAlignment.end with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.end,
spacing: 14.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
return parentData.offset;
}
expect(getOffset(box1).dx, equals(172.0));
expect(box1.size.width, equals(100.0));
expect(getOffset(box2).dx, equals(286.0));
expect(box2.size.width, equals(100.0));
expect(getOffset(box3).dx, equals(400.0));
expect(box3.size.width, equals(100.0));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(72.0));
expect(box1.size.height, equals(100.0));
expect(getOffset(box2).dy, equals(186.0));
expect(box2.size.height, equals(100.0));
expect(getOffset(box3).dy, equals(300.0));
expect(box3.size.height, equals(100.0));
});
test('MainAxisAlignment.center with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 14.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
return parentData.offset;
}
expect(getOffset(box1).dx, equals(86.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(314.0));
expect(box3.size.width, equals(100.0));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(36.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(264.0));
expect(box3.size.height, equals(100.0));
});
test('MainAxisAlignment.spaceEvenly with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
spacing: 14.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
return parentData.offset;
}
expect(getOffset(box1).dx, equals(43.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(357.0));
expect(box3.size.width, equals(100.0));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(18.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(282.0));
expect(box3.size.height, equals(100.0));
});
test('MainAxisAlignment.spaceAround with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceAround,
spacing: 14.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
return parentData.offset;
}
expect(getOffset(box1).dx, closeTo(28.6, 0.1));
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, closeTo(371.3, 0.1));
expect(box3.size.width, equals(100.0));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(12.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(288.0));
expect(box3.size.height, equals(100.0));
});
test('MainAxisAlignment.spaceBetween with RenderFlex.spacing', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 14.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
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));
flex.direction = Axis.vertical;
pumpFrame();
expect(getOffset(box1).dy, equals(0.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(300.0));
expect(box3.size.height, equals(100.0));
});
test('Fit.loose', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
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) {
final parentData = box.parentData! as FlexParentData;
parentData.flex = 1;
parentData.fit = fit;
}
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(200.0));
expect(box2.size.width, equals(100.0));
expect(getOffset(box3).dx, equals(400.0));
expect(box3.size.width, equals(100.0));
box1.additionalConstraints = const BoxConstraints.tightFor(width: 1000.0, height: 100.0);
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));
});
test('Flexible with MainAxisSize.min', () {
final box1 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box2 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final box3 = RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 100.0, height: 100.0),
);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex, constraints: const BoxConstraints(maxWidth: 500.0, maxHeight: 400.0));
Offset getOffset(RenderBox box) {
final parentData = box.parentData! as FlexParentData;
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) {
final parentData = box.parentData! as FlexParentData;
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));
});
test('MainAxisSize.min inside unconstrained', () {
FlutterError.onError = (FlutterErrorDetails details) => throw details.exception;
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.ltr, mainAxisSize: MainAxisSize.min);
final parent = RenderConstrainedOverflowBox(
minWidth: 0.0,
maxWidth: double.infinity,
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));
final box2ParentData = box2.parentData! as FlexParentData;
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', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.ltr, mainAxisSize: MainAxisSize.min);
final parent = RenderConstrainedOverflowBox(
minWidth: 0.0,
maxWidth: double.infinity,
minHeight: 0.0,
maxHeight: 400.0,
child: flex,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
final exceptions = <dynamic>[];
layout(
parent,
onErrors: () {
exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
},
);
expect(exceptions, isNotEmpty);
expect(exceptions.first, isFlutterError);
});
test('MainAxisSize.min inside unconstrained', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.ltr);
final parent = RenderConstrainedOverflowBox(
minWidth: 0.0,
maxWidth: double.infinity,
minHeight: 0.0,
maxHeight: 400.0,
child: flex,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.loose;
final exceptions = <dynamic>[];
layout(
parent,
onErrors: () {
exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
},
);
expect(exceptions, isNotEmpty);
expect(exceptions.first, isFlutterError);
});
test('MainAxisSize.min inside tightly constrained', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.rtl, mainAxisSize: MainAxisSize.min);
flex.addAll(<RenderBox>[box1, box2, box3]);
layout(flex);
expect(flex.constraints.hasTightWidth, isTrue);
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));
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));
});
test('Flex RTL', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
children: <RenderBox>[box1, box2, box3],
);
layout(flex);
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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();
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));
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));
});
test('children with no baselines are top-aligned', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
children: <RenderBox>[box1, box2],
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
verticalDirection: VerticalDirection.up,
);
layout(flex);
// Not start-aligned.
expect(box1.localToGlobal(Offset.zero).dy, 0.0);
expect(box2.localToGlobal(Offset.zero).dy, 0.0);
flex.verticalDirection = VerticalDirection.down;
pumpFrame();
expect(box1.localToGlobal(Offset.zero).dy, 0.0);
expect(box2.localToGlobal(Offset.zero).dy, 0.0);
});
test('Vertical Flex Baseline', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(
additionalConstraints: square,
child: RenderFlowBaselineTestBox()
..gridCount = 1
..baselinePlacer = (double height) => 10,
);
final box2 = RenderConstrainedBox(
additionalConstraints: square,
child: RenderFlowBaselineTestBox()
..gridCount = 1
..baselinePlacer = (double height) => 10,
);
RenderConstrainedBox filler() => RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
children: <RenderBox>[filler(), box1, filler(), box2, filler()],
direction: Axis.vertical,
);
layout(flex, phase: EnginePhase.paint);
final double flexHeight = flex.size.height;
// We can't call the getDistanceToBaseline method directly. Check the dry
// baseline instead, and in debug mode there are asserts that verify
// the two methods return the same results.
expect(flex.getDryBaseline(flex.constraints, TextBaseline.alphabetic), 100 + 10);
flex.mainAxisAlignment = MainAxisAlignment.end;
pumpFrame(phase: EnginePhase.paint);
expect(flex.getDryBaseline(flex.constraints, TextBaseline.alphabetic), flexHeight - 400 + 10);
flex.verticalDirection = VerticalDirection.up;
pumpFrame(phase: EnginePhase.paint);
expect(flex.getDryBaseline(flex.constraints, TextBaseline.alphabetic), 300 + 10);
flex.mainAxisAlignment = MainAxisAlignment.start;
pumpFrame(phase: EnginePhase.paint);
expect(flex.getDryBaseline(flex.constraints, TextBaseline.alphabetic), flexHeight - 200 + 10);
});
group('Intrinsics', () {
test('main axis intrinsics with RenderAspectRatio 1', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderAspectRatio(
aspectRatio: 1.0,
child: RenderConstrainedBox(additionalConstraints: square),
);
final flex = RenderFlex(textDirection: TextDirection.ltr);
flex.addAll(<RenderBox>[box1, box2, box3]);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.tight; // In intrinsics FlexFit.tight should have no effect.
expect(flex.getMinIntrinsicWidth(double.infinity), 300.0);
expect(flex.getMaxIntrinsicWidth(double.infinity), 300.0);
expect(flex.getMinIntrinsicWidth(300.0), 200.0 + 300.0);
expect(flex.getMaxIntrinsicWidth(300.0), 200.0 + 300.0);
expect(flex.getMinIntrinsicWidth(500.0), 200.0 + 500.0);
expect(flex.getMaxIntrinsicWidth(500.0), 200.0 + 500.0);
});
test('main/cross axis intrinsics in horizontal direction and RenderFlex.spacing', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(textDirection: TextDirection.ltr, spacing: 16.0);
flex.addAll(<RenderBox>[box1, box2, box3]);
expect(flex.getMinIntrinsicWidth(double.infinity), 332.0);
expect(flex.getMaxIntrinsicWidth(double.infinity), 332.0);
expect(flex.getMinIntrinsicHeight(double.infinity), 100.0);
expect(flex.getMaxIntrinsicHeight(double.infinity), 100.0);
expect(flex.getMinIntrinsicWidth(300.0), 332.0);
expect(flex.getMaxIntrinsicWidth(300.0), 332.0);
expect(flex.getMinIntrinsicHeight(300.0), 100.0);
expect(flex.getMaxIntrinsicHeight(300.0), 100.0);
expect(flex.getMinIntrinsicWidth(500.0), 332.0);
expect(flex.getMaxIntrinsicWidth(500.0), 332.0);
expect(flex.getMinIntrinsicHeight(500.0), 100.0);
expect(flex.getMaxIntrinsicHeight(500.0), 100.0);
});
test('main/cross axis intrinsics in vertical direction and RenderFlex.spacing', () {
const square = BoxConstraints.tightFor(width: 100.0, height: 100.0);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderConstrainedBox(additionalConstraints: square);
final box3 = RenderConstrainedBox(additionalConstraints: square);
final flex = RenderFlex(
textDirection: TextDirection.ltr,
direction: Axis.vertical,
spacing: 16.0,
);
flex.addAll(<RenderBox>[box1, box2, box3]);
expect(flex.getMinIntrinsicWidth(double.infinity), 100.0);
expect(flex.getMaxIntrinsicWidth(double.infinity), 100.0);
expect(flex.getMinIntrinsicHeight(double.infinity), 332.0);
expect(flex.getMaxIntrinsicHeight(double.infinity), 332.0);
expect(flex.getMinIntrinsicWidth(300.0), 100.0);
expect(flex.getMaxIntrinsicWidth(300.0), 100.0);
expect(flex.getMinIntrinsicHeight(300.0), 332.0);
expect(flex.getMaxIntrinsicHeight(300.0), 332.0);
expect(flex.getMinIntrinsicWidth(500.0), 100.0);
expect(flex.getMaxIntrinsicWidth(500.0), 100.0);
expect(flex.getMinIntrinsicHeight(500.0), 332.0);
expect(flex.getMaxIntrinsicHeight(500.0), 332.0);
});
test('cross axis intrinsics, with ascending flex flow layout', () {
const square = BoxConstraints.tightFor(width: 5.0, height: 5.0);
// 3 'A's separated by zero-width spaces. Max intrinsic width = 30, min intrinsic width = 10
final textSpan = TextSpan(
text: List<String>.filled(3, 'A').join('\u200B'),
style: const TextStyle(fontSize: 10),
);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderParagraph(textSpan, textDirection: TextDirection.ltr);
final box3 = RenderParagraph(textSpan, textDirection: TextDirection.ltr);
final flex = RenderFlex(textDirection: TextDirection.ltr);
flex.addAll(<RenderBox>[box1, box2, box3]);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.tight; // In intrinsics FlexFit.tight should have no effect.
final box3ParentData = box3.parentData! as FlexParentData;
box3ParentData.flex = 2;
box3ParentData.fit = FlexFit.tight; // In intrinsics FlexFit.tight should have no effect.
expect(flex.getMinIntrinsicHeight(double.infinity), 10.0);
expect(flex.getMaxIntrinsicHeight(double.infinity), 10.0);
// 95.0 is the max intrinsic width of the RenderFlex.
// width distribution = 5, 30, 60.
expect(flex.getMinIntrinsicHeight(95.0), 10.0);
expect(flex.getMaxIntrinsicHeight(95.0), 10.0);
expect(flex.getMinIntrinsicHeight(94.0), 20.0);
expect(flex.getMaxIntrinsicHeight(94.0), 20.0);
// width distribution = 5, 20, 40
expect(flex.getMinIntrinsicHeight(65.0), 20.0);
expect(flex.getMaxIntrinsicHeight(65.0), 20.0);
// width distribution = 5, 10, 20
expect(flex.getMinIntrinsicHeight(35.0), 30.0);
expect(flex.getMaxIntrinsicHeight(35.0), 30.0);
});
test('cross axis intrinsics, with descending flex flow layout', () {
const square = BoxConstraints.tightFor(width: 5.0, height: 5.0);
// 3 'A's separated by zero-width spaces. Max intrinsic width = 30, min intrinsic width = 10
final textSpan = TextSpan(
text: List<String>.filled(3, 'A').join('\u200B'),
style: const TextStyle(fontSize: 10),
);
final box1 = RenderConstrainedBox(additionalConstraints: square);
final box2 = RenderParagraph(textSpan, textDirection: TextDirection.ltr);
final box3 = RenderParagraph(textSpan, textDirection: TextDirection.ltr);
final flex = RenderFlex(textDirection: TextDirection.ltr);
flex.addAll(<RenderBox>[box1, box2, box3]);
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 2;
box2ParentData.fit = FlexFit.tight; // In intrinsics FlexFit.tight should have no effect.
final box3ParentData = box3.parentData! as FlexParentData;
box3ParentData.flex = 1;
box3ParentData.fit = FlexFit.tight; // In intrinsics FlexFit.tight should have no effect.
// The setup is exactly the same as the previous test, but the flex factors
// are swapped.
expect(flex.getMinIntrinsicHeight(double.infinity), 10.0);
expect(flex.getMaxIntrinsicHeight(double.infinity), 10.0);
// 95.0 is the max intrinsic width of the RenderFlex.
expect(flex.getMinIntrinsicHeight(95.0), 10.0);
expect(flex.getMaxIntrinsicHeight(95.0), 10.0);
// width distribution = 5, 40, 20.
expect(flex.getMinIntrinsicHeight(65.0), 20.0);
expect(flex.getMaxIntrinsicHeight(65.0), 20.0);
// width distribution = 5, 20, 10
expect(flex.getMinIntrinsicHeight(35.0), 30.0);
expect(flex.getMaxIntrinsicHeight(35.0), 30.0);
});
test('baseline aligned flex flow computeDryLayout', () {
// box1 has its baseline placed at the top of the box.
final box1 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => 0.0)
..gridCount = 10;
// box2 has its baseline placed at the bottom of the box.
final box2 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => height)
..gridCount = 10;
final flex = RenderFlex(
textDirection: TextDirection.ltr,
textBaseline: TextBaseline.alphabetic,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: <RenderBox>[box1, box2],
);
final box1ParentData = box1.parentData! as FlexParentData;
box1ParentData.flex = 2;
box1ParentData.fit = FlexFit.tight;
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.loose;
var size = const Size(200, 100);
// box 1 one line, box 2 two lines.
expect(flex.getDryLayout(BoxConstraints.loose(size)), const Size(200.0, 30.0));
expect(flex.getDryBaseline(BoxConstraints.loose(size), TextBaseline.alphabetic), 20.0);
size = const Size(300, 100);
// box 1 one line, box 2 one line.
expect(flex.getDryLayout(BoxConstraints.loose(size)), const Size(300.0, 20.0));
expect(flex.getDryBaseline(BoxConstraints.loose(size), TextBaseline.alphabetic), 10.0);
});
test('baseline aligned children cross intrinsic size', () {
// box1 has its baseline placed at the top of the box.
final box1 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => 0.0)
..gridCount = 10;
// box2 has its baseline placed at the bottom of the box.
final box2 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => height)
..gridCount = 10;
final flex = RenderFlex(
textDirection: TextDirection.ltr,
textBaseline: TextBaseline.alphabetic,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: <RenderBox>[box1, box2],
);
final box1ParentData = box1.parentData! as FlexParentData;
box1ParentData.flex = 2;
box1ParentData.fit = FlexFit.tight;
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.loose;
// box 1 one line, box 2 two lines.
expect(flex.getMaxIntrinsicHeight(200), 30);
expect(flex.getMinIntrinsicHeight(200), 30);
// box 1 one line, box 2 one line.
expect(flex.getMaxIntrinsicHeight(300), 20);
expect(flex.getMinIntrinsicHeight(300), 20);
});
test('children with no baselines do not affect the baseline location', () {
// box1 has its baseline placed at the bottom of the box.
final box1 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => height)
..gridCount = 10;
// box2 has its baseline placed at the bottom of the box.
final box2 = RenderFlowBaselineTestBox()
..baselinePlacer = ((double height) => null)
..gridCount = 10;
final flex = RenderFlex(
textDirection: TextDirection.ltr,
textBaseline: TextBaseline.alphabetic,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: <RenderBox>[box1, box2],
);
final box1ParentData = box1.parentData! as FlexParentData;
box1ParentData.flex = 2;
box1ParentData.fit = FlexFit.tight;
final box2ParentData = box2.parentData! as FlexParentData;
box2ParentData.flex = 1;
box2ParentData.fit = FlexFit.loose;
var size = const Size(200, 100);
// box 1 one line, box 2 two lines.
expect(flex.getDryLayout(BoxConstraints.loose(size)), const Size(200.0, 20.0));
expect(flex.getDryBaseline(BoxConstraints.loose(size), TextBaseline.alphabetic), 10.0);
size = const Size(300, 100);
// box 1 one line, box 2 one.
expect(flex.getDryLayout(BoxConstraints.loose(size)), const Size(300.0, 10.0));
expect(flex.getDryBaseline(BoxConstraints.loose(size), TextBaseline.alphabetic), 10.0);
});
});
test('Can call methods that check overflow even if overflow value is not set', () {
final exceptions = <dynamic>[];
final flex = RenderFlex(children: const <RenderBox>[]);
// This forces a check for _hasOverflow
expect(flex.toStringShort(), isNot(contains('OVERFLOWING')));
layout(
flex,
phase: EnginePhase.paint,
onErrors: () {
exceptions.addAll(TestRenderingFlutterBinding.instance.takeAllFlutterExceptions());
},
);
// 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));
// ignore: avoid_dynamic_calls
expect(exceptions.first.message, isNot(contains('Null check operator')));
});
test('Negative RenderFlex.spacing throws an exception', () {
final exceptions = <dynamic>[];
final box = RenderDecoratedBox(decoration: const BoxDecoration());
try {
RenderFlex(textDirection: TextDirection.ltr, spacing: -15.0, children: <RenderBox>[box]);
} catch (e) {
exceptions.add(e);
}
expect(exceptions, hasLength(1));
});
}
class RenderFlowBaselineTestBox extends RenderBox {
static const Size gridSize = Size(10, 10);
int gridCount = 0;
int lineGridCount(double width) {
final int gridsPerLine = width >= gridCount * gridSize.width
? gridCount
: width ~/ gridSize.width;
return math.max(1, gridsPerLine);
}
int lineCount(double width) => (gridCount / lineGridCount(width)).ceil();
double? Function(double height) baselinePlacer = (double height) => null;
@override
double computeMinIntrinsicWidth(double height) => gridSize.width;
@override
double computeMaxIntrinsicWidth(double height) => gridSize.width * gridCount;
@override
double computeMinIntrinsicHeight(double width) => gridSize.height * lineCount(width);
@override
double computeMaxIntrinsicHeight(double width) => computeMinIntrinsicHeight(width);
@override
Size computeDryLayout(covariant BoxConstraints constraints) {
return constraints.constrain(
Size(
gridSize.width * lineGridCount(constraints.maxWidth),
gridSize.height * lineCount(constraints.maxWidth),
),
);
}
@override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) =>
baselinePlacer(getDryLayout(constraints).height);
@override
double? computeDistanceToActualBaseline(TextBaseline baseline) => baselinePlacer(size.height);
@override
void performLayout() {
size = computeDryLayout(constraints);
}
}