Reland Alert dialog overflow spacing (#50675)
* Add ButtonBar.overflowButtonSpacing * Add AlertDialog overflow button spacing functionality
This commit is contained in:
parent
450fc25c1e
commit
a833effbc7
@ -65,9 +65,11 @@ class ButtonBar extends StatelessWidget {
|
|||||||
this.buttonAlignedDropdown,
|
this.buttonAlignedDropdown,
|
||||||
this.layoutBehavior,
|
this.layoutBehavior,
|
||||||
this.overflowDirection,
|
this.overflowDirection,
|
||||||
|
this.overflowButtonSpacing,
|
||||||
this.children = const <Widget>[],
|
this.children = const <Widget>[],
|
||||||
}) : assert(buttonMinWidth == null || buttonMinWidth >= 0.0),
|
}) : assert(buttonMinWidth == null || buttonMinWidth >= 0.0),
|
||||||
assert(buttonHeight == null || buttonHeight >= 0.0),
|
assert(buttonHeight == null || buttonHeight >= 0.0),
|
||||||
|
assert(overflowButtonSpacing == null || overflowButtonSpacing >= 0.0),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// How the children should be placed along the horizontal axis.
|
/// How the children should be placed along the horizontal axis.
|
||||||
@ -143,6 +145,22 @@ class ButtonBar extends StatelessWidget {
|
|||||||
/// default to [VerticalDirection.down].
|
/// default to [VerticalDirection.down].
|
||||||
final VerticalDirection overflowDirection;
|
final VerticalDirection overflowDirection;
|
||||||
|
|
||||||
|
/// The spacing between buttons when the button bar overflows.
|
||||||
|
///
|
||||||
|
/// If the [children] do not fit into a single row, they are
|
||||||
|
/// arranged into a column. This parameter provides additional
|
||||||
|
/// vertical space in between buttons when it does overflow.
|
||||||
|
///
|
||||||
|
/// Note that the button spacing may appear to be more than
|
||||||
|
/// the value provided. This is because most buttons adhere to the
|
||||||
|
/// [MaterialTapTargetSize] of 48px. So, even though a button
|
||||||
|
/// might visually be 36px in height, it might still take up to
|
||||||
|
/// 48px vertically.
|
||||||
|
///
|
||||||
|
/// If null then no spacing will be added in between buttons in
|
||||||
|
/// an overflow state.
|
||||||
|
final double overflowButtonSpacing;
|
||||||
|
|
||||||
/// The buttons to arrange horizontally.
|
/// The buttons to arrange horizontally.
|
||||||
///
|
///
|
||||||
/// Typically [RaisedButton] or [FlatButton] widgets.
|
/// Typically [RaisedButton] or [FlatButton] widgets.
|
||||||
@ -176,6 +194,7 @@ class ButtonBar extends StatelessWidget {
|
|||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
overflowButtonSpacing: overflowButtonSpacing,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
switch (buttonTheme.layoutBehavior) {
|
switch (buttonTheme.layoutBehavior) {
|
||||||
@ -226,6 +245,7 @@ class _ButtonBarRow extends Flex {
|
|||||||
TextDirection textDirection,
|
TextDirection textDirection,
|
||||||
VerticalDirection overflowDirection = VerticalDirection.down,
|
VerticalDirection overflowDirection = VerticalDirection.down,
|
||||||
TextBaseline textBaseline,
|
TextBaseline textBaseline,
|
||||||
|
this.overflowButtonSpacing,
|
||||||
}) : super(
|
}) : super(
|
||||||
children: children,
|
children: children,
|
||||||
direction: direction,
|
direction: direction,
|
||||||
@ -237,6 +257,8 @@ class _ButtonBarRow extends Flex {
|
|||||||
textBaseline: textBaseline,
|
textBaseline: textBaseline,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final double overflowButtonSpacing;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RenderButtonBarRow createRenderObject(BuildContext context) {
|
_RenderButtonBarRow createRenderObject(BuildContext context) {
|
||||||
return _RenderButtonBarRow(
|
return _RenderButtonBarRow(
|
||||||
@ -247,6 +269,7 @@ class _ButtonBarRow extends Flex {
|
|||||||
textDirection: getEffectiveTextDirection(context),
|
textDirection: getEffectiveTextDirection(context),
|
||||||
verticalDirection: verticalDirection,
|
verticalDirection: verticalDirection,
|
||||||
textBaseline: textBaseline,
|
textBaseline: textBaseline,
|
||||||
|
overflowButtonSpacing: overflowButtonSpacing,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +282,8 @@ class _ButtonBarRow extends Flex {
|
|||||||
..crossAxisAlignment = crossAxisAlignment
|
..crossAxisAlignment = crossAxisAlignment
|
||||||
..textDirection = getEffectiveTextDirection(context)
|
..textDirection = getEffectiveTextDirection(context)
|
||||||
..verticalDirection = verticalDirection
|
..verticalDirection = verticalDirection
|
||||||
..textBaseline = textBaseline;
|
..textBaseline = textBaseline
|
||||||
|
..overflowButtonSpacing = overflowButtonSpacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +313,9 @@ class _RenderButtonBarRow extends RenderFlex {
|
|||||||
@required TextDirection textDirection,
|
@required TextDirection textDirection,
|
||||||
VerticalDirection verticalDirection = VerticalDirection.down,
|
VerticalDirection verticalDirection = VerticalDirection.down,
|
||||||
TextBaseline textBaseline,
|
TextBaseline textBaseline,
|
||||||
|
this.overflowButtonSpacing,
|
||||||
}) : assert(textDirection != null),
|
}) : assert(textDirection != null),
|
||||||
|
assert(overflowButtonSpacing == null || overflowButtonSpacing >= 0),
|
||||||
super(
|
super(
|
||||||
children: children,
|
children: children,
|
||||||
direction: direction,
|
direction: direction,
|
||||||
@ -302,6 +328,7 @@ class _RenderButtonBarRow extends RenderFlex {
|
|||||||
);
|
);
|
||||||
|
|
||||||
bool _hasCheckedLayoutWidth = false;
|
bool _hasCheckedLayoutWidth = false;
|
||||||
|
double overflowButtonSpacing;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BoxConstraints get constraints {
|
BoxConstraints get constraints {
|
||||||
@ -391,6 +418,9 @@ class _RenderButtonBarRow extends RenderFlex {
|
|||||||
child = childParentData.previousSibling;
|
child = childParentData.previousSibling;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (overflowButtonSpacing != null && child != null)
|
||||||
|
currentHeight += overflowButtonSpacing;
|
||||||
}
|
}
|
||||||
size = constraints.constrain(Size(constraints.maxWidth, currentHeight));
|
size = constraints.constrain(Size(constraints.maxWidth, currentHeight));
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import 'ink_well.dart';
|
|||||||
import 'material.dart';
|
import 'material.dart';
|
||||||
import 'material_localizations.dart';
|
import 'material_localizations.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
import 'theme_data.dart';
|
||||||
|
|
||||||
// Examples can assume:
|
// Examples can assume:
|
||||||
// enum Department { treasury, state }
|
// enum Department { treasury, state }
|
||||||
@ -223,6 +224,7 @@ class AlertDialog extends StatelessWidget {
|
|||||||
this.actions,
|
this.actions,
|
||||||
this.actionsPadding = EdgeInsets.zero,
|
this.actionsPadding = EdgeInsets.zero,
|
||||||
this.actionsOverflowDirection,
|
this.actionsOverflowDirection,
|
||||||
|
this.actionsOverflowButtonSpacing,
|
||||||
this.buttonPadding,
|
this.buttonPadding,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.elevation,
|
this.elevation,
|
||||||
@ -342,6 +344,22 @@ class AlertDialog extends StatelessWidget {
|
|||||||
/// * [ButtonBar], which [actions] configures to lay itself out.
|
/// * [ButtonBar], which [actions] configures to lay itself out.
|
||||||
final VerticalDirection actionsOverflowDirection;
|
final VerticalDirection actionsOverflowDirection;
|
||||||
|
|
||||||
|
/// The spacing between [actions] when the button bar overflows.
|
||||||
|
///
|
||||||
|
/// If the widgets in [actions] do not fit into a single row, they are
|
||||||
|
/// arranged into a column. This parameter provides additional
|
||||||
|
/// vertical space in between buttons when it does overflow.
|
||||||
|
///
|
||||||
|
/// Note that the button spacing may appear to be more than
|
||||||
|
/// the value provided. This is because most buttons adhere to the
|
||||||
|
/// [MaterialTapTargetSize] of 48px. So, even though a button
|
||||||
|
/// might visually be 36px in height, it might still take up to
|
||||||
|
/// 48px vertically.
|
||||||
|
///
|
||||||
|
/// If null then no spacing will be added in between buttons in
|
||||||
|
/// an overflow state.
|
||||||
|
final double actionsOverflowButtonSpacing;
|
||||||
|
|
||||||
/// The padding that surrounds each button in [actions].
|
/// The padding that surrounds each button in [actions].
|
||||||
///
|
///
|
||||||
/// This is different from [actionsPadding], which defines the padding
|
/// This is different from [actionsPadding], which defines the padding
|
||||||
@ -445,6 +463,7 @@ class AlertDialog extends StatelessWidget {
|
|||||||
child: ButtonBar(
|
child: ButtonBar(
|
||||||
buttonPadding: buttonPadding,
|
buttonPadding: buttonPadding,
|
||||||
overflowDirection: actionsOverflowDirection,
|
overflowDirection: actionsOverflowDirection,
|
||||||
|
overflowButtonSpacing: actionsOverflowButtonSpacing,
|
||||||
children: actions,
|
children: actions,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -570,7 +570,60 @@ void main() {
|
|||||||
final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
|
final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
|
||||||
final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
|
final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
|
||||||
// Second [Container] should appear above first container.
|
// Second [Container] should appear above first container.
|
||||||
expect(containerTwoRect.bottom, containerOneRect.top);
|
expect(containerTwoRect.bottom, lessThanOrEqualTo(containerOneRect.top));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'ButtonBar has no spacing by default when overflowing',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final Key keyOne = UniqueKey();
|
||||||
|
final Key keyTwo = UniqueKey();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ButtonBar(
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
// Set padding to zero to align buttons with edge of button bar.
|
||||||
|
buttonPadding: EdgeInsets.zero,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(key: keyOne, height: 50.0, width: 500.0),
|
||||||
|
Container(key: keyTwo, height: 50.0, width: 500.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
|
||||||
|
final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
|
||||||
|
expect(containerOneRect.bottom, containerTwoRect.top);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
"ButtonBar's children respects overflowButtonSpacing when overflowing",
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final Key keyOne = UniqueKey();
|
||||||
|
final Key keyTwo = UniqueKey();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: ButtonBar(
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
// Set padding to zero to align buttons with edge of button bar.
|
||||||
|
buttonPadding: EdgeInsets.zero,
|
||||||
|
// Set the overflow button spacing to ensure add some space between
|
||||||
|
// buttons in an overflow case.
|
||||||
|
overflowButtonSpacing: 10.0,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(key: keyOne, height: 50.0, width: 500.0),
|
||||||
|
Container(key: keyTwo, height: 50.0, width: 500.0),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Rect containerOneRect = tester.getRect(find.byKey(keyOne));
|
||||||
|
final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo));
|
||||||
|
expect(containerOneRect.bottom, containerTwoRect.top - 10.0);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -557,7 +557,7 @@ void main() {
|
|||||||
); // right
|
); // right
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dialogs can set the vertical direction of actions', (WidgetTester tester) async {
|
testWidgets('Dialogs can set the vertical direction of overflowing actions', (WidgetTester tester) async {
|
||||||
final GlobalKey key1 = GlobalKey();
|
final GlobalKey key1 = GlobalKey();
|
||||||
final GlobalKey key2 = GlobalKey();
|
final GlobalKey key2 = GlobalKey();
|
||||||
|
|
||||||
@ -589,7 +589,74 @@ void main() {
|
|||||||
final Rect buttonOneRect = tester.getRect(find.byKey(key1));
|
final Rect buttonOneRect = tester.getRect(find.byKey(key1));
|
||||||
final Rect buttonTwoRect = tester.getRect(find.byKey(key2));
|
final Rect buttonTwoRect = tester.getRect(find.byKey(key2));
|
||||||
// Second [RaisedButton] should appear above the first.
|
// Second [RaisedButton] should appear above the first.
|
||||||
expect(buttonTwoRect.bottom, buttonOneRect.top);
|
expect(buttonTwoRect.bottom, lessThanOrEqualTo(buttonOneRect.top));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Dialogs have no spacing by default for overflowing actions', (WidgetTester tester) async {
|
||||||
|
final GlobalKey key1 = GlobalKey();
|
||||||
|
final GlobalKey key2 = GlobalKey();
|
||||||
|
|
||||||
|
final AlertDialog dialog = AlertDialog(
|
||||||
|
title: const Text('title'),
|
||||||
|
content: const Text('content'),
|
||||||
|
actions: <Widget>[
|
||||||
|
RaisedButton(
|
||||||
|
key: key1,
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Text('Looooooooooooooong button 1'),
|
||||||
|
),
|
||||||
|
RaisedButton(
|
||||||
|
key: key2,
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Text('Looooooooooooooong button 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildAppWithDialog(dialog),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final Rect buttonOneRect = tester.getRect(find.byKey(key1));
|
||||||
|
final Rect buttonTwoRect = tester.getRect(find.byKey(key2));
|
||||||
|
expect(buttonOneRect.bottom, buttonTwoRect.top);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Dialogs can set the button spacing of overflowing actions', (WidgetTester tester) async {
|
||||||
|
final GlobalKey key1 = GlobalKey();
|
||||||
|
final GlobalKey key2 = GlobalKey();
|
||||||
|
|
||||||
|
final AlertDialog dialog = AlertDialog(
|
||||||
|
title: const Text('title'),
|
||||||
|
content: const Text('content'),
|
||||||
|
actions: <Widget>[
|
||||||
|
RaisedButton(
|
||||||
|
key: key1,
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Text('Looooooooooooooong button 1'),
|
||||||
|
),
|
||||||
|
RaisedButton(
|
||||||
|
key: key2,
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Text('Looooooooooooooong button 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
actionsOverflowButtonSpacing: 10.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildAppWithDialog(dialog),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final Rect buttonOneRect = tester.getRect(find.byKey(key1));
|
||||||
|
final Rect buttonTwoRect = tester.getRect(find.byKey(key2));
|
||||||
|
expect(buttonOneRect.bottom, buttonTwoRect.top - 10.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dialogs removes MediaQuery padding and view insets', (WidgetTester tester) async {
|
testWidgets('Dialogs removes MediaQuery padding and view insets', (WidgetTester tester) async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user