mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add OverflowBar.alignment property (#85050)
This commit is contained in:
parent
b79dd40bad
commit
9e082f6ce9
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user