Unverified Commit 3f179d80 authored by MH Johnson's avatar MH Johnson Committed by GitHub

[Material] Add custom shape parameter for Dialogs. (#23443)

* [Material] Add custom shape parameter for Dialogs.

* [Material] Add custom shape parameter for Dialogs.

* Address Hans' first round comments.

* Address Hans' first round comments.

* Address Hans' Second round comments.
parent d8cbb802
...@@ -42,6 +42,7 @@ class Dialog extends StatelessWidget { ...@@ -42,6 +42,7 @@ class Dialog extends StatelessWidget {
this.child, this.child,
this.insetAnimationDuration = const Duration(milliseconds: 100), this.insetAnimationDuration = const Duration(milliseconds: 100),
this.insetAnimationCurve = Curves.decelerate, this.insetAnimationCurve = Curves.decelerate,
this.shape,
}) : super(key: key); }) : super(key: key);
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
...@@ -61,10 +62,23 @@ class Dialog extends StatelessWidget { ...@@ -61,10 +62,23 @@ class Dialog extends StatelessWidget {
/// Defaults to [Curves.fastOutSlowIn]. /// Defaults to [Curves.fastOutSlowIn].
final Curve insetAnimationCurve; final Curve insetAnimationCurve;
/// {@template flutter.material.dialog.shape}
/// The shape of this dialog's border.
///
/// Defines the dialog's [Material.shape].
///
/// The default shape is a [RoundedRectangleBorder] with a radius of 2.0.
/// {@endtemplate}
final ShapeBorder shape;
Color _getColor(BuildContext context) { Color _getColor(BuildContext context) {
return Theme.of(context).dialogBackgroundColor; return Theme.of(context).dialogBackgroundColor;
} }
// TODO(johnsonmh): Update default dialog border radius to 4.0 to match material spec.
static const RoundedRectangleBorder _defaultDialogShape =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0)));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedPadding( return AnimatedPadding(
...@@ -85,6 +99,7 @@ class Dialog extends StatelessWidget { ...@@ -85,6 +99,7 @@ class Dialog extends StatelessWidget {
color: _getColor(context), color: _getColor(context),
type: MaterialType.card, type: MaterialType.card,
child: child, child: child,
shape: shape ?? _defaultDialogShape,
), ),
), ),
), ),
...@@ -172,6 +187,7 @@ class AlertDialog extends StatelessWidget { ...@@ -172,6 +187,7 @@ class AlertDialog extends StatelessWidget {
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0), this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
this.actions, this.actions,
this.semanticLabel, this.semanticLabel,
this.shape,
}) : assert(contentPadding != null), }) : assert(contentPadding != null),
super(key: key); super(key: key);
...@@ -235,6 +251,9 @@ class AlertDialog extends StatelessWidget { ...@@ -235,6 +251,9 @@ class AlertDialog extends StatelessWidget {
/// value is used. /// value is used.
final String semanticLabel; final String semanticLabel;
/// {@macro flutter.material.dialog.shape}
final ShapeBorder shape;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
...@@ -295,7 +314,7 @@ class AlertDialog extends StatelessWidget { ...@@ -295,7 +314,7 @@ class AlertDialog extends StatelessWidget {
child: dialogChild child: dialogChild
); );
return Dialog(child: dialogChild); return Dialog(child: dialogChild, shape: shape);
} }
} }
...@@ -440,6 +459,7 @@ class SimpleDialog extends StatelessWidget { ...@@ -440,6 +459,7 @@ class SimpleDialog extends StatelessWidget {
this.children, this.children,
this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0), this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
this.semanticLabel, this.semanticLabel,
this.shape,
}) : assert(titlePadding != null), }) : assert(titlePadding != null),
assert(contentPadding != null), assert(contentPadding != null),
super(key: key); super(key: key);
...@@ -494,6 +514,9 @@ class SimpleDialog extends StatelessWidget { ...@@ -494,6 +514,9 @@ class SimpleDialog extends StatelessWidget {
/// value is used. /// value is used.
final String semanticLabel; final String semanticLabel;
/// {@macro flutter.material.dialog.shape}
final ShapeBorder shape;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasMaterialLocalizations(context));
...@@ -546,7 +569,7 @@ class SimpleDialog extends StatelessWidget { ...@@ -546,7 +569,7 @@ class SimpleDialog extends StatelessWidget {
label: label, label: label,
child: dialogChild, child: dialogChild,
); );
return Dialog(child: dialogChild); return Dialog(child: dialogChild, shape: shape);
} }
} }
......
...@@ -10,47 +10,52 @@ import 'package:matcher/matcher.dart'; ...@@ -10,47 +10,52 @@ import 'package:matcher/matcher.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
MaterialApp _appWithAlertDialog(WidgetTester tester, AlertDialog dialog, {ThemeData theme}) {
return MaterialApp(
theme: theme,
home: Material(
child: Builder(
builder: (BuildContext context) {
return Center(
child: RaisedButton(
child: const Text('X'),
onPressed: () {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return dialog;
},
);
}
)
);
}
)
),
);
}
const ShapeBorder _defaultDialogShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0)));
void main() { void main() {
testWidgets('Dialog is scrollable', (WidgetTester tester) async { testWidgets('Dialog is scrollable', (WidgetTester tester) async {
bool didPressOk = false; bool didPressOk = false;
final AlertDialog dialog = AlertDialog(
await tester.pumpWidget( content: Container(
MaterialApp( height: 5000.0,
home: Material( width: 300.0,
child: Builder( color: Colors.green[500],
builder: (BuildContext context) { ),
return Center( actions: <Widget>[
child: RaisedButton( FlatButton(
child: const Text('X'), onPressed: () {
onPressed: () { didPressOk = true;
showDialog<void>( },
context: context, child: const Text('OK')
builder: (BuildContext context) {
return AlertDialog(
content: Container(
height: 5000.0,
width: 300.0,
color: Colors.green[500],
),
actions: <Widget>[
FlatButton(
onPressed: () {
didPressOk = true;
},
child: const Text('OK')
)
],
);
},
);
}
)
);
}
)
) )
) ],
); );
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pump(); // start animation await tester.pump(); // start animation
...@@ -62,46 +67,76 @@ void main() { ...@@ -62,46 +67,76 @@ void main() {
}); });
testWidgets('Dialog background color', (WidgetTester tester) async { testWidgets('Dialog background color', (WidgetTester tester) async {
const AlertDialog dialog = AlertDialog(
await tester.pumpWidget( title: Text('Title'),
MaterialApp( content: Text('Y'),
theme: ThemeData(brightness: Brightness.dark), actions: <Widget>[ ],
home: Material(
child: Builder(
builder: (BuildContext context) {
return Center(
child: RaisedButton(
child: const Text('X'),
onPressed: () {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return const AlertDialog(
title: Text('Title'),
content: Text('Y'),
actions: <Widget>[ ],
);
},
);
},
),
);
},
),
),
),
); );
await tester.pumpWidget(_appWithAlertDialog(tester, dialog, theme: ThemeData(brightness: Brightness.dark)));
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pump(); // start animation await tester.pump(); // start animation
await tester.pump(const Duration(seconds: 1)); await tester.pump(const Duration(seconds: 1));
final StatefulElement widget = tester.element(find.byType(Material).last); final StatefulElement widget = tester.element(
find.descendant(of: find.byType(AlertDialog), matching: find.byType(Material)));
final Material materialWidget = widget.state.widget; final Material materialWidget = widget.state.widget;
//first and second expect check that the material is the dialog's one
expect(materialWidget.type, MaterialType.card);
expect(materialWidget.elevation, 24);
expect(materialWidget.color, Colors.grey[800]); expect(materialWidget.color, Colors.grey[800]);
expect(materialWidget.shape, _defaultDialogShape);
});
testWidgets('Custom dialog shape', (WidgetTester tester) async {
const RoundedRectangleBorder customBorder =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)));
const AlertDialog dialog = AlertDialog(
actions: <Widget>[ ],
shape: customBorder,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pump(); // start animation
await tester.pump(const Duration(seconds: 1));
final StatefulElement widget = tester.element(
find.descendant(of: find.byType(AlertDialog), matching: find.byType(Material)));
final Material materialWidget = widget.state.widget;
expect(materialWidget.shape, customBorder);
});
testWidgets('Null dialog shape', (WidgetTester tester) async {
const AlertDialog dialog = AlertDialog(
actions: <Widget>[ ],
shape: null,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pump(); // start animation
await tester.pump(const Duration(seconds: 1));
final StatefulElement widget = tester.element(
find.descendant(of: find.byType(AlertDialog), matching: find.byType(Material)));
final Material materialWidget = widget.state.widget;
expect(materialWidget.shape, _defaultDialogShape);
});
testWidgets('Rectangular dialog shape', (WidgetTester tester) async {
const ShapeBorder customBorder = Border();
const AlertDialog dialog = AlertDialog(
actions: <Widget>[ ],
shape: customBorder,
);
await tester.pumpWidget(_appWithAlertDialog(tester, dialog));
await tester.tap(find.text('X'));
await tester.pump(); // start animation
await tester.pump(const Duration(seconds: 1));
final StatefulElement widget = tester.element(
find.descendant(of: find.byType(AlertDialog), matching: find.byType(Material)));
final Material materialWidget = widget.state.widget;
expect(materialWidget.shape, customBorder);
}); });
testWidgets('Simple dialog control test', (WidgetTester tester) async { testWidgets('Simple dialog control test', (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