Unverified Commit a833effb authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Reland Alert dialog overflow spacing (#50675)

* Add ButtonBar.overflowButtonSpacing

* Add AlertDialog overflow button spacing functionality
parent 450fc25c
...@@ -65,9 +65,11 @@ class ButtonBar extends StatelessWidget { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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'; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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 {
......
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