Unverified Commit 9e082f6c authored by Hans Muller's avatar Hans Muller Committed by GitHub

Add OverflowBar.alignment property (#85050)

parent b79dd40b
......@@ -103,6 +103,7 @@ class OverflowBar extends MultiChildRenderObjectWidget {
OverflowBar({
Key? key,
this.spacing = 0.0,
this.alignment,
this.overflowSpacing = 0.0,
this.overflowAlignment = OverflowBarAlignment.start,
this.overflowDirection = VerticalDirection.down,
......@@ -125,6 +126,33 @@ class OverflowBar extends MultiChildRenderObjectWidget {
/// Defaults to 0.0.
final double spacing;
/// Defines the [children]'s horizontal layout according to the same
/// rules as for [Row.mainAxisAlignment].
///
/// If this property is non-null, and the [children], separated by
/// [spacing], fit within the available width, then the overflow
/// bar will be as wide as possible. If the children do not fit
/// within the available width, then this property is ignored and
/// [overflowAlignment] applies instead.
///
/// If this property is null (the default) then the overflow bar
/// will be no wider than needed to layout the [children] separated
/// by [spacing], modulo the incoming constraints.
///
/// If [alignment] is one of [MainAxisAlignment.spaceAround],
/// [MainAxisAlignment.spaceBetween], or
/// [MainAxisAlignment.spaceEvenly], then the [spacing] parameter is
/// only used to see if the horizontal layout will overflow.
///
/// Defaults to null.
///
/// See also:
///
/// * [overflowAlignment], the horizontal alignment of the [children] within
/// the vertical "overflow" layout.
///
final MainAxisAlignment? alignment;
/// The height of the gap between [children] in the vertical
/// "overflow" layout.
///
......@@ -163,6 +191,9 @@ class OverflowBar extends MultiChildRenderObjectWidget {
///
/// See also:
///
/// * [alignment], which defines the [children]'s horizontal layout
/// (according to the same rules as for [Row.mainAxisAlignment]) when
/// the children, separated by [spacing], fit within the available space.
/// * [overflowDirection], which defines the order that the
/// [OverflowBar]'s children appear in, if the horizontal layout
/// overflows.
......@@ -221,6 +252,7 @@ class OverflowBar extends MultiChildRenderObjectWidget {
RenderObject createRenderObject(BuildContext context) {
return _RenderOverflowBar(
spacing: spacing,
alignment: alignment,
overflowSpacing: overflowSpacing,
overflowAlignment: overflowAlignment,
overflowDirection: overflowDirection,
......@@ -233,6 +265,7 @@ class OverflowBar extends MultiChildRenderObjectWidget {
void updateRenderObject(BuildContext context, RenderObject renderObject) {
(renderObject as _RenderOverflowBar)
..spacing = spacing
..alignment = alignment
..overflowSpacing = overflowSpacing
..overflowAlignment = overflowAlignment
..overflowDirection = overflowDirection
......@@ -244,6 +277,7 @@ class OverflowBar extends MultiChildRenderObjectWidget {
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('spacing', spacing, defaultValue: 0));
properties.add(EnumProperty<MainAxisAlignment>('alignment', alignment, defaultValue: null));
properties.add(DoubleProperty('overflowSpacing', overflowSpacing, defaultValue: 0));
properties.add(EnumProperty<OverflowBarAlignment>('overflowAlignment', overflowAlignment, defaultValue: OverflowBarAlignment.start));
properties.add(EnumProperty<VerticalDirection>('overflowDirection', overflowDirection, defaultValue: VerticalDirection.down));
......@@ -259,6 +293,7 @@ class _RenderOverflowBar extends RenderBox
_RenderOverflowBar({
List<RenderBox>? children,
double spacing = 0.0,
MainAxisAlignment? alignment,
double overflowSpacing = 0.0,
OverflowBarAlignment overflowAlignment = OverflowBarAlignment.start,
VerticalDirection overflowDirection = VerticalDirection.down,
......@@ -270,6 +305,7 @@ class _RenderOverflowBar extends RenderBox
assert(textDirection != null),
assert(clipBehavior != null),
_spacing = spacing,
_alignment = alignment,
_overflowSpacing = overflowSpacing,
_overflowAlignment = overflowAlignment,
_overflowDirection = overflowDirection,
......@@ -288,6 +324,15 @@ class _RenderOverflowBar extends RenderBox
markNeedsLayout();
}
MainAxisAlignment? get alignment => _alignment;
MainAxisAlignment? _alignment;
set alignment (MainAxisAlignment? value) {
if (_alignment == value)
return;
_alignment = value;
markNeedsLayout();
}
double get overflowSpacing => _overflowSpacing;
double _overflowSpacing;
set overflowSpacing (double value) {
......@@ -456,7 +501,8 @@ class _RenderOverflowBar extends RenderBox
if (actualWidth > constraints.maxWidth) {
return constraints.constrain(Size(constraints.maxWidth, y - overflowSpacing));
} else {
return constraints.constrain(Size(actualWidth, maxChildHeight));
final double overallWidth = alignment == null ? actualWidth : constraints.maxWidth;
return constraints.constrain(Size(overallWidth, maxChildHeight));
}
}
......@@ -510,10 +556,42 @@ class _RenderOverflowBar extends RenderBox
}
size = constraints.constrain(Size(constraints.maxWidth, y - overflowSpacing));
} else {
// Default horizontal layout.
size = constraints.constrain(Size(actualWidth, maxChildHeight));
// Default horizontal layout
child = firstChild;
double x = rtl ? size.width - child!.size.width : 0;
final double firstChildWidth = child!.size.width;
final double overallWidth = alignment == null ? actualWidth : constraints.maxWidth;
size = constraints.constrain(Size(overallWidth, maxChildHeight));
late double x; // initial value: origin of the first child
double layoutSpacing = spacing; // space between children
switch (alignment) {
case null:
x = rtl ? size.width - firstChildWidth : 0;
break;
case MainAxisAlignment.start:
x = rtl ? size.width - firstChildWidth : 0;
break;
case MainAxisAlignment.center:
final double halfRemainingWidth = (size.width - actualWidth) / 2;
x = rtl ? size.width - halfRemainingWidth - firstChildWidth : halfRemainingWidth;
break;
case MainAxisAlignment.end:
x = rtl ? actualWidth - firstChildWidth : size.width - actualWidth;
break;
case MainAxisAlignment.spaceBetween:
layoutSpacing = (size.width - childrenWidth) / (childCount - 1);
x = rtl ? size.width - firstChildWidth : 0;
break;
case MainAxisAlignment.spaceAround:
layoutSpacing = childCount > 0 ? (size.width - childrenWidth) / childCount : 0;
x = rtl ? size.width - layoutSpacing / 2 - firstChildWidth : layoutSpacing / 2;
break;
case MainAxisAlignment.spaceEvenly:
layoutSpacing = (size.width - childrenWidth) / (childCount + 1);
x = rtl ? size.width - layoutSpacing - firstChildWidth : layoutSpacing;
break;
}
while (child != null) {
final _OverflowBarParentData childParentData = child.parentData! as _OverflowBarParentData;
childParentData.offset = Offset(x, (maxChildHeight - child.size.height) / 2);
......@@ -522,11 +600,11 @@ class _RenderOverflowBar extends RenderBox
// the origin of the next child for RTL: subtract the width of the next
// child (if there is one).
if (!rtl) {
x += child.size.width + spacing;
x += child.size.width + layoutSpacing;
}
child = childAfter(child);
if (rtl && child != null) {
x -= child.size.width + spacing;
x -= child.size.width + layoutSpacing;
}
}
}
......
......@@ -9,6 +9,7 @@ void main() {
testWidgets('OverflowBar documented defaults', (WidgetTester tester) async {
final OverflowBar bar = OverflowBar();
expect(bar.spacing, 0);
expect(bar.alignment, null);
expect(bar.overflowSpacing, 0);
expect(bar.overflowDirection, VerticalDirection.down);
expect(bar.textDirection, null);
......@@ -271,4 +272,73 @@ void main() {
expect(tester.getTopLeft(find.byKey(key1)).dx, 680);
expect(tester.getTopLeft(find.byKey(key2)).dx, 600);
});
testWidgets('OverflowBar with alignment should match Row with mainAxisAlignment', (WidgetTester tester) async {
final Key key0 = UniqueKey();
final Key key1 = UniqueKey();
final Key key2 = UniqueKey();
// This list of children appears in a Row and an OverflowBar, so each
// find.byKey() for key0, key1, key2 returns two widgets.
final List<Widget> children = <Widget>[
SizedBox(key: key0, width: 50, height: 50),
SizedBox(key: key1, width: 70, height: 50),
SizedBox(key: key2, width: 80, height: 50),
];
const List<MainAxisAlignment> allAlignments = <MainAxisAlignment>[
MainAxisAlignment.start,
MainAxisAlignment.center,
MainAxisAlignment.end,
MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceAround,
MainAxisAlignment.spaceEvenly,
];
const List<TextDirection> allTextDirections = <TextDirection>[
TextDirection.ltr,
TextDirection.rtl,
];
Widget buildFrame(MainAxisAlignment alignment, TextDirection textDirection) {
return Directionality(
textDirection: textDirection,
child: Column(
children: <Widget>[
OverflowBar(
alignment: alignment,
children: children,
),
Row(
mainAxisAlignment: alignment,
children: children,
),
],
),
);
}
// Each key from key0, key1, key2 maps to one child in the OverflowBar
// and a matching child in the Row. We expect the children to be the
// same size and for their left and right edges to align.
void testLayout() {
expect(tester.getSize(find.byType(OverflowBar)), const Size(800, 50));
for (final Key key in <Key>[key0, key1, key2]) {
final Finder matchingChildren = find.byKey(key);
expect(matchingChildren.evaluate().length, 2);
final Rect rect0 = tester.getRect(matchingChildren.first);
final Rect rect1 = tester.getRect(matchingChildren.last);
expect(rect0.size, rect1.size);
expect(rect0.left, rect1.left);
expect(rect0.right, rect1.right);
}
}
for (final MainAxisAlignment alignment in allAlignments) {
for (final TextDirection textDirection in allTextDirections) {
await tester.pumpWidget(buildFrame(alignment, textDirection));
testLayout();
}
}
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment