diff --git a/packages/flutter/lib/src/rendering/flex.dart b/packages/flutter/lib/src/rendering/flex.dart index c41fced1b1..ac7df91bcb 100644 --- a/packages/flutter/lib/src/rendering/flex.dart +++ b/packages/flutter/lib/src/rendering/flex.dart @@ -215,19 +215,19 @@ enum MainAxisAlignment { /// after the first and last child. spaceEvenly; - (double leadingSpace, double betweenSpace) _distributeSpace(double freeSpace, int itemCount, bool flipped) { + (double leadingSpace, double betweenSpace) _distributeSpace(double freeSpace, int itemCount, bool flipped, double spacing) { assert(itemCount >= 0); return switch (this) { - MainAxisAlignment.start => flipped ? (freeSpace, 0.0) : (0.0, 0.0), + MainAxisAlignment.start => flipped ? (freeSpace, spacing) : (0.0, spacing), - MainAxisAlignment.end => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, !flipped), - MainAxisAlignment.spaceBetween when itemCount < 2 => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, flipped), - MainAxisAlignment.spaceAround when itemCount == 0 => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, flipped), + MainAxisAlignment.end => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, !flipped, spacing), + MainAxisAlignment.spaceBetween when itemCount < 2 => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, flipped, spacing), + MainAxisAlignment.spaceAround when itemCount == 0 => MainAxisAlignment.start._distributeSpace(freeSpace, itemCount, flipped, spacing), - MainAxisAlignment.center => (freeSpace / 2.0, 0.0), - MainAxisAlignment.spaceBetween => (0.0, freeSpace / (itemCount - 1)), - MainAxisAlignment.spaceAround => (freeSpace / itemCount / 2, freeSpace / itemCount), - MainAxisAlignment.spaceEvenly => (freeSpace / (itemCount + 1), freeSpace / (itemCount + 1)), + MainAxisAlignment.center => (freeSpace / 2.0, spacing), + MainAxisAlignment.spaceBetween => (0.0, freeSpace / (itemCount - 1) + spacing), + MainAxisAlignment.spaceAround => (freeSpace / itemCount / 2, freeSpace / itemCount + spacing), + MainAxisAlignment.spaceEvenly => (freeSpace / (itemCount + 1), freeSpace / (itemCount + 1) + spacing), }; } } @@ -390,6 +390,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin= 0.0) { addAll(children); } @@ -588,6 +591,69 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin _spacing; + double _spacing; + set spacing (double value) { + if (_spacing == value) { + return; + } + _spacing = value; + markNeedsLayout(); + } + @override void setupParentData(RenderBox child) { if (child.parentData is! FlexParentData) { @@ -597,15 +663,15 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin 0) { @@ -1064,7 +1131,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty('verticalDirection', verticalDirection, defaultValue: null)); properties.add(EnumProperty('textBaseline', textBaseline, defaultValue: null)); + properties.add(DoubleProperty('spacing', spacing, defaultValue: null)); } } diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 3a8c3ac1d3..49676bcf8c 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -4604,6 +4604,7 @@ class Flex extends MultiChildRenderObjectWidget { this.verticalDirection = VerticalDirection.down, this.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be this.clipBehavior = Clip.none, + this.spacing = 0.0, super.children, }) : assert(!identical(crossAxisAlignment, CrossAxisAlignment.baseline) || textBaseline != null, 'textBaseline is required if you specify the crossAxisAlignment with CrossAxisAlignment.baseline'); // Cannot use == in the assert above instead of identical because of https://github.com/dart-lang/language/issues/1811. @@ -4710,6 +4711,9 @@ class Flex extends MultiChildRenderObjectWidget { /// Defaults to [Clip.none]. final Clip clipBehavior; + /// {@macro flutter.rendering.RenderFlex.spacing} + final double spacing; + bool get _needTextDirection { switch (direction) { case Axis.horizontal: @@ -4751,6 +4755,7 @@ class Flex extends MultiChildRenderObjectWidget { verticalDirection: verticalDirection, textBaseline: textBaseline, clipBehavior: clipBehavior, + spacing: spacing, ); } @@ -4764,7 +4769,8 @@ class Flex extends MultiChildRenderObjectWidget { ..textDirection = getEffectiveTextDirection(context) ..verticalDirection = verticalDirection ..textBaseline = textBaseline - ..clipBehavior = clipBehavior; + ..clipBehavior = clipBehavior + ..spacing = spacing; } @override @@ -4777,6 +4783,8 @@ class Flex extends MultiChildRenderObjectWidget { properties.add(EnumProperty('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty('verticalDirection', verticalDirection, defaultValue: VerticalDirection.down)); properties.add(EnumProperty('textBaseline', textBaseline, defaultValue: null)); + properties.add(EnumProperty('clipBehavior', clipBehavior, defaultValue: Clip.none)); + properties.add(DoubleProperty('spacing', spacing, defaultValue: 0.0)); } } @@ -4983,6 +4991,7 @@ class Row extends Flex { super.textDirection, super.verticalDirection, super.textBaseline, // NO DEFAULT: we don't know what the text's baseline should be + super.spacing, super.children, }) : super( direction: Axis.horizontal, @@ -5174,6 +5183,7 @@ class Column extends Flex { super.textDirection, super.verticalDirection, super.textBaseline, + super.spacing, super.children, }) : super( direction: Axis.vertical, diff --git a/packages/flutter/test/rendering/flex_test.dart b/packages/flutter/test/rendering/flex_test.dart index 702761d318..d53de584dc 100644 --- a/packages/flutter/test/rendering/flex_test.dart +++ b/packages/flutter/test/rendering/flex_test.dart @@ -109,6 +109,29 @@ void main() { expect(flex.getMaxIntrinsicWidth(100.0), equals(0.0)); }); + test('Vertical Overflow with RenderFlex.spacing', () { + final RenderConstrainedBox flexible = RenderConstrainedBox( + additionalConstraints: const BoxConstraints.expand(), + ); + final RenderFlex flex = RenderFlex( + direction: Axis.vertical, + spacing: 16.0, + children: [ + RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(height: 200.0)), + flexible, + ], + ); + final FlexParentData flexParentData = flexible.parentData! as FlexParentData; + flexParentData.flex = 1; + const BoxConstraints 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 RenderConstrainedBox flexible = RenderConstrainedBox( additionalConstraints: const BoxConstraints.expand(), @@ -131,6 +154,29 @@ void main() { expect(flex.getMaxIntrinsicWidth(100.0), equals(200.0)); }); + test('Horizontal Overflow with RenderFlex.spacing', () { + final RenderConstrainedBox flexible = RenderConstrainedBox( + additionalConstraints: const BoxConstraints.expand(), + ); + final RenderFlex flex = RenderFlex( + textDirection: TextDirection.ltr, + spacing: 12.0, + children: [ + RenderConstrainedBox(additionalConstraints: const BoxConstraints.tightFor(width: 200.0)), + flexible, + ], + ); + final FlexParentData flexParentData = flexible.parentData! as FlexParentData; + flexParentData.flex = 1; + const BoxConstraints 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 RenderFlex flex = RenderFlex( direction: Axis.vertical, @@ -162,7 +208,8 @@ void main() { ' mainAxisAlignment: start\n' ' mainAxisSize: max\n' ' crossAxisAlignment: center\n' - ' verticalDirection: down\n', + ' verticalDirection: down\n' + ' spacing: 0.0\n', ), ); }); @@ -253,6 +300,215 @@ void main() { expect(box3.size.height, equals(100.0)); }); + test('MainAxisAlignment.start with RenderFlex.spacing', () { + 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, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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.end, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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.center, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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.spaceAround, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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, + spacing: 14.0, + ); + flex.addAll([box1, box2, box3]); + layout(flex, constraints: const BoxConstraints( + maxWidth: 500.0, + maxHeight: 400.0, + )); + Offset getOffset(RenderBox box) { + final FlexParentData 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 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)); @@ -719,6 +975,61 @@ void main() { expect(flex.getMaxIntrinsicWidth(500.0), 200.0 + 500.0); }); + test('main/cross axis intrinsics in horizontal direction and RenderFlex.spacing', () { + const BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0); + 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, + spacing: 16.0, + ); + flex.addAll([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 BoxConstraints square = BoxConstraints.tightFor(width: 100.0, height: 100.0); + 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, + direction: Axis.vertical, + spacing: 16.0, + ); + flex.addAll([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 BoxConstraints 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 @@ -909,6 +1220,21 @@ void main() { // ignore: avoid_dynamic_calls expect(exceptions.first.message, isNot(contains('Null check operator'))); }); + + test('Negative RenderFlex.spacing throws an exception', () { + final List exceptions = []; + final RenderDecoratedBox box = RenderDecoratedBox(decoration: const BoxDecoration()); + try { + RenderFlex( + textDirection: TextDirection.ltr, + spacing: -15.0, + children: [box], + ); + } catch (e) { + exceptions.add(e); + } + expect(exceptions, hasLength(1)); + }); } class RenderFlowBaselineTestBox extends RenderBox { diff --git a/packages/flutter/test/widgets/flex_test.dart b/packages/flutter/test/widgets/flex_test.dart index fed96741a7..a13df4a762 100644 --- a/packages/flutter/test/widgets/flex_test.dart +++ b/packages/flutter/test/widgets/flex_test.dart @@ -7,6 +7,41 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + Widget constrainedFlex({ + required Axis direction, + required MainAxisAlignment mainAxisAlignment, + required double spacing, + }) { + return Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: Flex( + direction: direction, + mainAxisAlignment: mainAxisAlignment, + spacing: spacing, + children: const [ + SizedBox( + width: 50.0, + height: 50.0, + ), + SizedBox( + width: 50.0, + height: 50.0, + ), + SizedBox( + width: 50.0, + height: 50.0, + ), + ], + ), + ), + ), + ); + } + testWidgets('Can hit test flex children of stacks', (WidgetTester tester) async { bool didReceiveTap = false; await tester.pumpWidget( @@ -147,4 +182,164 @@ void main() { const Column(); const Row(); }); + + testWidgets('Default Flex.spacing value', (WidgetTester tester) async { + await tester.pumpWidget(const Flex(direction: Axis.vertical)); + + final Flex flex = tester.widget(find.byType(Flex)); + expect(flex.spacing, 0.0); + }); + + testWidgets('Can update Flex.spacing value', (WidgetTester tester) async { + Widget buildFlex({ required double spacing }) { + return Center( + child: Directionality( + textDirection: TextDirection.ltr, + child: Flex( + spacing: spacing, + direction: Axis.vertical, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 100.0, + width: 100.0, + color: const Color(0xFFFF0000), + ), + Container( + height: 100.0, + width: 100.0, + color: const Color(0xFF0000FF), + ), + Container( + height: 100.0, + width: 100.0, + color: const Color(0xff00FF00), + ), + ], + ), + ), + ); + } + await tester.pumpWidget(buildFlex(spacing: 8.0)); + + RenderFlex renderObject = tester.allRenderObjects.whereType().first; + expect(renderObject.spacing, equals(8.0)); + expect(tester.getSize(find.byType(Flex)).width, equals(100.0)); + expect(tester.getSize(find.byType(Flex)).height, equals(316.0)); + + await tester.pumpWidget(buildFlex(spacing: 18.0)); + + renderObject = tester.allRenderObjects.whereType().first; + expect(renderObject.spacing, equals(18.0)); + expect(tester.getSize(find.byType(Flex)).width, equals(100.0)); + expect(tester.getSize(find.byType(Flex)).height, equals(336.0)); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.start and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.start, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.start, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.end and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.end, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.end, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.center and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.center, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.center, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.spaceAround and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceAround, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceAround, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.spaceEvenly and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); + + testWidgets('Overconstrained Flex with MainAxisAlignment.spaceBetween and spacing', (WidgetTester tester) async { + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 50.0, + )); + // 50.0 * 3 (children) + 50.0 * 2 (spacing) = 250.0 < 300.0 (constraints) + expect(tester.takeException(), isNull); + + await tester.pumpWidget(constrainedFlex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 100.0, + )); + // 50.0 * 3 (children) + 100.0 * 2 (spacing) = 350.0 > 300.0 (constraints) + expect(tester.takeException(), isAssertionError); + }); } diff --git a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart index ade0902793..446a44fef5 100644 --- a/packages/flutter_tools/test/integration.shard/overall_experience_test.dart +++ b/packages/flutter_tools/test/integration.shard/overall_experience_test.dart @@ -278,6 +278,7 @@ void main() { ' crossAxisAlignment: center', ' textDirection: ltr', ' verticalDirection: down', + ' spacing: 0.0', '◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤', '════════════════════════════════════════════════════════════════════════════════════════════════════', '',