Commit dc4a1638 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Specialize AlertDialog and SimpleDialog (#6098)

We were trying to cram too much functionality in to the Dialog widget. Now we
have AlertDialog and SimpleDialog to cover to two different kinds of dialogs in
the spec.
parent 517ddab3
......@@ -69,10 +69,10 @@ class DialogDemoState extends State<DialogDemo> {
_selectedTime = new TimeOfDay(hour: now.hour, minute: now.minute);
}
void showDemoDialog/*<T>*/({ BuildContext context, Dialog dialog }) {
void showDemoDialog/*<T>*/({ BuildContext context, Widget child }) {
showDialog/*<T>*/(
context: context,
child: dialog
child: child
)
.then((dynamic/*=T*/ value) { // The value passed to Navigator.pop() or null.
if (value != null) {
......@@ -101,7 +101,7 @@ class DialogDemoState extends State<DialogDemo> {
onPressed: () {
showDemoDialog/*<DialogDemoAction>*/(
context: context,
dialog: new Dialog(
child: new AlertDialog(
content: new Text(
_alertWithoutTitleText,
style: dialogTextStyle
......@@ -125,7 +125,7 @@ class DialogDemoState extends State<DialogDemo> {
onPressed: () {
showDemoDialog/*<DialogDemoAction>*/(
context: context,
dialog: new Dialog(
child: new AlertDialog(
title: new Text('Use Google\'s location service?'),
content: new Text(
_alertWithTitleText,
......@@ -150,30 +150,27 @@ class DialogDemoState extends State<DialogDemo> {
onPressed: () {
showDemoDialog/*<String>*/(
context: context,
dialog: new Dialog(
child: new SimpleDialog(
title: new Text('Set backup account'),
contentPadding: const EdgeInsets.only(top: 12.0, bottom: 16.0),
content: new Column(
children: <Widget>[
new DialogDemoItem(
icon: Icons.account_circle,
color: theme.primaryColor,
text: 'username@gmail.com',
onPressed: () { Navigator.pop(context, 'username@gmail.com'); }
),
new DialogDemoItem(
icon: Icons.account_circle,
color: theme.primaryColor,
text: 'user02@gmail.com',
onPressed: () { Navigator.pop(context, 'user02@gmail.com'); }
),
new DialogDemoItem(
icon: Icons.add_circle,
text: 'add account',
color: theme.disabledColor
)
]
)
children: <Widget>[
new DialogDemoItem(
icon: Icons.account_circle,
color: theme.primaryColor,
text: 'username@gmail.com',
onPressed: () { Navigator.pop(context, 'username@gmail.com'); }
),
new DialogDemoItem(
icon: Icons.account_circle,
color: theme.primaryColor,
text: 'user02@gmail.com',
onPressed: () { Navigator.pop(context, 'user02@gmail.com'); }
),
new DialogDemoItem(
icon: Icons.add_circle,
text: 'add account',
color: theme.disabledColor
)
]
)
);
}
......
......@@ -115,7 +115,7 @@ class FullScreenDialogDemoState extends State<FullScreenDialogDemo> {
showDialog(
context: context,
child: new Dialog(
child: new AlertDialog(
content: new Text(
'Discard new event?',
style: dialogTextStyle
......
......@@ -55,7 +55,7 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> {
void _showMessage() {
showDialog(
context: context,
child: new Dialog(
child: new AlertDialog(
content: new Text('You tapped the floating action button.'),
actions: <Widget>[
new FlatButton(
......
......@@ -49,7 +49,7 @@ class UpdaterState extends State<Updater> {
final ThemeData theme = Theme.of(context);
final TextStyle dialogTextStyle =
theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color);
return new Dialog(
return new AlertDialog(
title: new Text('Update Flutter Gallery?'),
content: new Text('A newer version is available.', style: dialogTextStyle),
actions: <Widget>[
......
......@@ -19,7 +19,7 @@ enum StockHomeTab { market, portfolio }
class _NotImplementedDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Dialog(
return new AlertDialog(
title: new Text('Not Implemented'),
content: new Text('This feature has not yet been implemented.'),
actions: <Widget>[
......
......@@ -67,7 +67,7 @@ class StockSettingsState extends State<StockSettings> {
case StockMode.pessimistic:
showDialog(
context: context,
child: new Dialog(
child: new AlertDialog(
title: new Text("Change mode?"),
content: new Text("Optimistic mode means everything is awesome. Are you sure you can handle that?"),
actions: <Widget>[
......
......@@ -273,7 +273,7 @@ class AboutDialog extends StatelessWidget {
];
if (children != null)
body.addAll(children);
return new Dialog(
return new AlertDialog(
content: new Block(
children: body
),
......
......@@ -51,7 +51,8 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
@override
Widget build(BuildContext context) {
return new Dialog(
// TODO(abarth): Use Dialog directly.
return new AlertDialog(
content: new DatePicker(
selectedDate: _selectedDate,
firstDate: config.firstDate,
......
......@@ -12,13 +12,17 @@ import 'colors.dart';
import 'material.dart';
import 'theme.dart';
/// A material design dialog
/// A material design dialog.
///
/// Typically passed as the child widget to [showDialog], which displays the
/// dialog.
/// This dialog widget does not have any opinion about the contents of the
/// dialog. Rather than using this widget directly, consider using [AlertDialog]
/// or [SimpleDialog], which implement specific kinds of material design
/// dialogs.
///
/// See also:
///
/// * [AlertDialog]
/// * [SimpleDialog]
/// * [showDialog]
/// * <https://www.google.com/design/spec/components/dialogs.html>
class Dialog extends StatelessWidget {
......@@ -26,6 +30,71 @@ class Dialog extends StatelessWidget {
///
/// Typically used in conjunction with [showDialog].
Dialog({
Key key,
this.child,
}) : super(key: key);
final Widget child;
Color _getColor(BuildContext context) {
Brightness brightness = Theme.of(context).brightness;
assert(brightness != null);
switch (brightness) {
case Brightness.light:
return Colors.white;
case Brightness.dark:
return Colors.grey[800];
}
return null;
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Container(
margin: new EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
child: new ConstrainedBox(
constraints: new BoxConstraints(minWidth: 280.0),
child: new Material(
elevation: 24,
color: _getColor(context),
type: MaterialType.card,
child: child
)
)
)
);
}
}
/// A material design alert dialog.
///
/// An alert dialog informs the user about situations that require
/// acknowledgement. An alert dialog has an optional title and an optional list
/// of actions. The title is displayed above the content and the actions are
/// displayed below the content.
///
/// If the content is too large to fit on the screen vertically, the dialog will
/// display the title and the actions and let the content overflow. Consider
/// using a scrolling widget, such as [Block], for [content] to avoid overflow.
///
/// For dialogs that offer the user a choice between several options, consider
/// using a [SimpleDialog].
///
/// Typically passed as the child widget to [showDialog], which displays the
/// dialog.
///
/// See also:
///
/// * [SimpleDialog]
/// * [Dialog]
/// * [showDialog]
/// * <https://material.google.com/components/dialogs.html#dialogs-alerts>
class AlertDialog extends StatelessWidget {
/// Creates an alert dialog.
///
/// Typically used in conjunction with [showDialog].
AlertDialog({
Key key,
this.title,
this.titlePadding,
......@@ -67,18 +136,6 @@ class Dialog extends StatelessWidget {
/// These widgets will be wrapped in a [ButtonBar].
final List<Widget> actions;
Color _getColor(BuildContext context) {
Brightness brightness = Theme.of(context).brightness;
switch (brightness) {
case Brightness.light:
return Colors.white;
case Brightness.dark:
return Colors.grey[800];
}
assert(brightness != null);
return null;
}
@override
Widget build(BuildContext context) {
final List<Widget> children = new List<Widget>();
......@@ -96,13 +153,11 @@ class Dialog extends StatelessWidget {
if (content != null) {
children.add(new Flexible(
fit: FlexFit.loose,
child: new ScrollableViewport(
child: new Padding(
padding: contentPadding ?? const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.subhead,
child: content
)
child: new Padding(
padding: contentPadding ?? const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.subhead,
child: content
)
)
));
......@@ -117,22 +172,104 @@ class Dialog extends StatelessWidget {
));
}
return new Center(
child: new Container(
margin: new EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
return new Dialog(
child: new IntrinsicWidth(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children
)
)
);
}
}
/// A simple material design dialog.
///
/// A simple dialog offers the user a choice between several options. A simple
/// dialog has an optional title that is displayed above the choices.
///
/// For dialogs that inform the user about a situation, consider using an
/// [AlertDialog].
///
/// Typically passed as the child widget to [showDialog], which displays the
/// dialog.
///
/// See also:
///
/// * [AlertDialog]
/// * [Dialog]
/// * [showDialog]
/// * <https://material.google.com/components/dialogs.html#dialogs-simple-dialogs>
class SimpleDialog extends StatelessWidget {
/// Creates a simple dialog.
///
/// Typically used in conjunction with [showDialog].
SimpleDialog({
Key key,
this.title,
this.titlePadding,
this.children,
this.contentPadding,
}) : super(key: key);
/// The (optional) title of the dialog is displayed in a large font at the top
/// of the dialog.
///
/// Typically a [Text] widget.
final Widget title;
/// Padding around the title.
///
/// Uses material design default if none is supplied. If there is no title, no
/// padding will be provided.
final EdgeInsets titlePadding;
/// The (optional) content of the dialog is displayed in a [Block] underneath
/// the title.
///
/// The children are assumed to have 8.0 pixels of vertical and 24.0 pixels of
/// horizontal padding internally.
final List<Widget> children;
/// Padding around the content.
///
/// Uses material design default if none is supplied.
final EdgeInsets contentPadding;
@override
Widget build(BuildContext context) {
final List<Widget> body = <Widget>[];
if (title != null) {
body.add(new Padding(
padding: titlePadding ?? const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.title,
child: title
)
));
}
if (children != null) {
body.add(new Flexible(
fit: FlexFit.loose,
child: new Block(
padding: contentPadding ?? const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
children: children
)
));
}
return new Dialog(
child: new IntrinsicWidth(
stepWidth: 56.0,
child: new ConstrainedBox(
constraints: new BoxConstraints(minWidth: 280.0),
child: new Material(
elevation: 24,
color: _getColor(context),
type: MaterialType.card,
child: new IntrinsicWidth(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children
)
)
constraints: const BoxConstraints(minWidth: 280.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: body
)
)
)
......
......@@ -47,7 +47,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
@override
Widget build(BuildContext context) {
return new Dialog(
// TODO(abarth): Use Dialog directly.
return new AlertDialog(
content: new TimePicker(
selectedTime: _selectedTime,
onChanged: _handleTimeChanged
......
......@@ -20,7 +20,7 @@ void main() {
onPressed: () {
showDialog(
context: context,
child: new Dialog(
child: new AlertDialog(
content: new Container(
height: 5000.0,
width: 300.0,
......
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