[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.
This commit is contained in:
parent
d8cbb80206
commit
3f179d80f6
@ -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 {
|
|||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
child: dialogChild
|
child: dialogChild
|
||||||
);
|
);
|
||||||
|
|
||||||
return Dialog(child: dialogChild);
|
return Dialog(child: dialogChild, shape: shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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 {
|
|||||||
/// 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 {
|
|||||||
label: label,
|
label: label,
|
||||||
child: dialogChild,
|
child: dialogChild,
|
||||||
);
|
);
|
||||||
return Dialog(child: dialogChild);
|
return Dialog(child: dialogChild, shape: shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,9 @@ import 'package:matcher/matcher.dart';
|
|||||||
|
|
||||||
import '../widgets/semantics_tester.dart';
|
import '../widgets/semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
MaterialApp _appWithAlertDialog(WidgetTester tester, AlertDialog dialog, {ThemeData theme}) {
|
||||||
testWidgets('Dialog is scrollable', (WidgetTester tester) async {
|
return MaterialApp(
|
||||||
bool didPressOk = false;
|
theme: theme,
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Material(
|
home: Material(
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@ -26,7 +23,24 @@ void main() {
|
|||||||
showDialog<void>(
|
showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return dialog;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShapeBorder _defaultDialogShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0)));
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Dialog is scrollable', (WidgetTester tester) async {
|
||||||
|
bool didPressOk = false;
|
||||||
|
final AlertDialog dialog = AlertDialog(
|
||||||
content: Container(
|
content: Container(
|
||||||
height: 5000.0,
|
height: 5000.0,
|
||||||
width: 300.0,
|
width: 300.0,
|
||||||
@ -41,16 +55,7 @@ void main() {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
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() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Dialog background color', (WidgetTester tester) async {
|
testWidgets('Dialog background color', (WidgetTester tester) async {
|
||||||
|
const AlertDialog dialog = AlertDialog(
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
theme: ThemeData(brightness: Brightness.dark),
|
|
||||||
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'),
|
title: Text('Title'),
|
||||||
content: Text('Y'),
|
content: Text('Y'),
|
||||||
actions: <Widget>[ ],
|
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user