Commit b2fa6c25 authored by Adam Barth's avatar Adam Barth

Fix the padding and space for FlatButton and RaisedButton (#3650)

Instead of incorporating the margin into the button, introduce a ButtonBar
widget that supplies the proper spacing between the buttons. Also, make these
buttons more configurable via ButtonTheme so that dialogs can change the
minWidth and padding of the buttons as required by the spec.

Fixes #1843
Fixes #3184
parent f8d76d18
......@@ -190,8 +190,8 @@ class Launcher extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.center,
return new ButtonBar(
alignment: MainAxisAlignment.center,
children: items
);
}
......
......@@ -96,8 +96,8 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
Widget buildRaisedButton() {
return new Align(
alignment: new FractionalOffset(0.5, 0.4),
child: new Row(
mainAxisAlignment: MainAxisAlignment.collapse,
child: new ButtonBar(
alignment: MainAxisAlignment.collapse,
children: <Widget>[
new RaisedButton(
child: new Text('RAISED BUTTON'),
......@@ -116,8 +116,8 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
Widget buildFlatButton() {
return new Align(
alignment: new FractionalOffset(0.5, 0.4),
child: new Row(
mainAxisAlignment: MainAxisAlignment.collapse,
child: new ButtonBar(
alignment: MainAxisAlignment.collapse,
children: <Widget>[
new FlatButton(
child: new Text('FLAT BUTTON'),
......
......@@ -44,18 +44,17 @@ class TravelDestinationItem extends StatelessWidget {
static final double height = 328.0;
final TravelDestination destination;
@override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
TextStyle titleStyle = theme.textTheme.headline.copyWith(color: Colors.white);
TextStyle descriptionStyle = theme.textTheme.subhead;
TextStyle buttonStyle = theme.textTheme.button.copyWith(color: theme.primaryColor);
return new SizedBox(
height: height,
child: new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// photo and title
new SizedBox(
......@@ -83,33 +82,35 @@ class TravelDestinationItem extends StatelessWidget {
// description and share/expore buttons
new Flexible(
child: new Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// three line description
new Text(destination.description[0], style: descriptionStyle),
new Text(destination.description[1], style: descriptionStyle),
new Text(destination.description[2], style: descriptionStyle),
// share, explore buttons
new Flexible(
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(right: 16.0),
child: new Text('SHARE', style: buttonStyle)
),
new Text('EXPLORE', style: buttonStyle)
]
)
)
]
)
)
)
),
// share, explore buttons
// TODO(abarth): The theme and the bar should be part of card.
new ButtonTheme.footer(
child: new ButtonBar(
alignment: MainAxisAlignment.start,
children: <Widget>[
new FlatButton(
child: new Text('SHARE'),
onPressed: () { /* do nothing */ }
),
new FlatButton(
child: new Text('EXPLORE'),
onPressed: () { /* do nothing */ }
),
]
)
),
]
)
)
......
......@@ -39,6 +39,7 @@ class _DatePickerDemoState extends State<DatePickerDemo> {
body: new Column(
children: <Widget>[
new Text(new DateFormat.yMMMd().format(_selectedDate)),
new SizedBox(height: 20.0),
new RaisedButton(
onPressed: _handleSelectDate,
child: new Text('SELECT DATE')
......
......@@ -33,14 +33,15 @@ class _TimePickerDemoState extends State<TimePickerDemo> {
return new Scaffold(
appBar: new AppBar(title: new Text('Time picker')),
body: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('$_selectedTime'),
new SizedBox(height: 20.0),
new RaisedButton(
onPressed: _handleSelectTime,
child: new Text('SELECT TIME')
),
],
mainAxisAlignment: MainAxisAlignment.center
]
)
);
}
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
/// Flutter widgets implementing Material Design.
///
///
/// To use, import `package:flutter/material.dart`.
///
/// See
......@@ -15,6 +15,7 @@ export 'src/material/app.dart';
export 'src/material/app_bar.dart';
export 'src/material/bottom_sheet.dart';
export 'src/material/button.dart';
export 'src/material/button_bar.dart';
export 'src/material/card.dart';
export 'src/material/checkbox.dart';
export 'src/material/chip.dart';
......
......@@ -92,7 +92,7 @@ class AppBar extends StatelessWidget {
/// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details.
final Widget flexibleSpace;
/// A horizontal strip of tabs to display at the bottom of the app bar.
/// A horizontal bar of tabs to display at the bottom of the app bar.
final TabBar<dynamic> tabBar;
/// The z-coordinate at which to place this app bar.
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'button.dart';
import 'dialog.dart';
import 'flat_button.dart';
import 'raised_button.dart';
/// A horizontal arrangement of buttons.
///
/// Places the buttons horizontally according to the padding in the current
/// [ButtonTheme].
///
/// Used by [Dialog] to arrange the actions at the bottom of the dialog.
///
/// See also:
///
/// * [RaisedButton]
/// * [FlatButton]
/// * [Dialog]
/// * [ButtonTheme]
class ButtonBar extends StatelessWidget {
/// Creates a button bar.
///
/// The alignment argument defaults to [MainAxisAlignment.end].
ButtonBar({
Key key,
this.alignment: MainAxisAlignment.end,
this.children
}) : super(key: key);
/// How the children should be placed along the horizontal axis.
final MainAxisAlignment alignment;
/// The buttons to arrange horizontally.
///
/// Typically [RaisedButton] or [FlatButton] widgets.
final List<Widget> children;
@override
Widget build(BuildContext context) {
// We divide by 4.0 because we want half of the average of the left and right padding.
final double paddingUnit = ButtonTheme.of(context).padding.horizontal / 4.0;
return new Padding(
padding: new EdgeInsets.symmetric(
vertical: 2.0 * paddingUnit,
horizontal: paddingUnit
),
child: new Row(
mainAxisAlignment: alignment,
children: children.map((Widget child) {
return new Padding(
padding: new EdgeInsets.symmetric(horizontal: paddingUnit),
child: child
);
}).toList()
)
);
}
}
......@@ -9,8 +9,6 @@ import 'package:flutter/widgets.dart';
import 'dialog.dart';
import 'date_picker.dart';
import 'flat_button.dart';
import 'theme.dart';
import 'theme_data.dart';
class _DatePickerDialog extends StatefulWidget {
_DatePickerDialog({
......@@ -53,7 +51,6 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return new Dialog(
content: new DatePicker(
selectedDate: _selectedDate,
......@@ -64,12 +61,10 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
contentPadding: EdgeInsets.zero,
actions: <Widget>[
new FlatButton(
textColor: theme.accentColor,
child: new Text('CANCEL'),
onPressed: _handleCancel
),
new FlatButton(
textColor: theme.accentColor,
child: new Text('OK'),
onPressed: _handleOk
),
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:flutter/widgets.dart';
import 'button.dart';
import 'button_bar.dart';
import 'colors.dart';
import 'material.dart';
import 'theme.dart';
......@@ -91,13 +92,10 @@ class Dialog extends StatelessWidget {
}
if (actions != null) {
dialogBody.add(new ButtonTheme(
color: ButtonColor.accent,
child: new Container(
child: new Row(
children: actions,
mainAxisAlignment: MainAxisAlignment.end
)
dialogBody.add(new ButtonTheme.footer(
child: new ButtonBar(
alignment: MainAxisAlignment.end,
children: actions
)
));
}
......
......@@ -33,27 +33,36 @@ import 'theme.dart';
/// * [RaisedButton]
/// * [DropDownButton]
/// * <https://www.google.com/design/spec/components/buttons.html>
class FlatButton extends MaterialButton {
class FlatButton extends StatelessWidget {
FlatButton({
Key key,
Widget child,
ThemeBrightness colorBrightness,
ButtonColor textTheme,
Color textColor,
Color disabledTextColor,
this.onPressed,
this.textColor,
this.disabledTextColor,
this.color,
this.disabledColor,
VoidCallback onPressed
}) : super(key: key,
child: child,
colorBrightness: colorBrightness,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
onPressed: onPressed);
this.textTheme,
this.colorBrightness,
this.child
}) : super(key: key);
/// The callback that is invoked when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback onPressed;
/// The color to use for this button's text.
///
/// Defaults to the color determined by the [textTheme].
final Color textColor;
/// The color to use for this button's text when the button cannot be pressed.
///
/// Defaults to a color derived from the [Theme].
final Color disabledTextColor;
/// The color of the button, as printed on the [Material]. Defaults to null,
/// meaning transparent.
/// meaning that the color is automatically derived from the [Theme].
final Color color;
/// The color of the button when the button is disabled. Buttons are disabled
......@@ -61,18 +70,32 @@ class FlatButton extends MaterialButton {
/// value.
final Color disabledColor;
@override
_FlatButtonState createState() => new _FlatButtonState();
}
/// The color scheme to use for this button's text.
///
/// Defaults to the button color from [ButtonTheme].
final ButtonTextTheme textTheme;
class _FlatButtonState extends MaterialButtonState<FlatButton> {
@override
int get elevation => 0;
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final ThemeBrightness colorBrightness;
/// The widget below this widget in the tree.
final Widget child;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// enable a button, set its [onPressed] property to a non-null value.
bool get enabled => onPressed != null;
@override
Color getColor(BuildContext context) {
if (!config.enabled)
return config.disabledColor;
return config.color;
Widget build(BuildContext context) {
return new MaterialButton(
onPressed: onPressed,
textColor: enabled ? textColor : disabledTextColor,
color: enabled ? color : disabledColor,
textTheme: textTheme,
colorBrightness: colorBrightness,
child: child
);
}
}
......@@ -31,21 +31,23 @@ import 'theme.dart';
/// * [DropDownButton]
/// * [FloatingActionButton]
/// * <https://www.google.com/design/spec/components/buttons.html>
class RaisedButton extends MaterialButton {
class RaisedButton extends StatelessWidget {
RaisedButton({
Key key,
Widget child,
ThemeBrightness colorBrightness,
this.onPressed,
this.color,
this.disabledColor,
this.elevation: 2,
this.highlightElevation: 8,
this.disabledElevation: 0,
VoidCallback onPressed
}) : super(key: key,
child: child,
colorBrightness: colorBrightness,
onPressed: onPressed);
this.colorBrightness,
this.child
}) : super(key: key);
/// The callback that is invoked when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback onPressed;
/// The color of the button, as printed on the [Material]. Defaults to null,
/// meaning that the color is automatically derived from the [Theme].
......@@ -62,34 +64,33 @@ class RaisedButton extends MaterialButton {
final int elevation;
/// The z-coordinate at which to place this button when highlighted.
///
/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12, 16, 24
final int highlightElevation;
/// The z-coordinate at which to place this button when disabled.
///
/// The following elevations have defined shadows: 1, 2, 3, 4, 6, 8, 9, 12, 16, 24
final int disabledElevation;
@override
_RaisedButtonState createState() => new _RaisedButtonState();
}
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final ThemeBrightness colorBrightness;
class _RaisedButtonState extends MaterialButtonState<RaisedButton> {
@override
int get elevation {
if (config.enabled) {
if (highlight)
return config.highlightElevation;
return config.elevation;
} else {
return config.disabledElevation;
}
}
/// The widget below this widget in the tree.
final Widget child;
@override
Color getColor(BuildContext context) {
if (config.enabled) {
return config.color ?? Theme.of(context).buttonColor;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// enable a button, set its [onPressed] property to a non-null value.
bool get enabled => onPressed != null;
Color _getColor(BuildContext context) {
if (enabled) {
return color ?? Theme.of(context).buttonColor;
} else {
if (config.disabledColor != null)
return config.disabledColor;
if (disabledColor != null)
return disabledColor;
switch (Theme.of(context).brightness) {
case ThemeBrightness.light:
return Colors.black12;
......@@ -98,4 +99,16 @@ class _RaisedButtonState extends MaterialButtonState<RaisedButton> {
}
}
}
@override
Widget build(BuildContext context) {
return new MaterialButton(
onPressed: onPressed,
color: _getColor(context),
elevation: enabled ? elevation : disabledElevation,
highlightElevation: enabled ? highlightElevation : disabledElevation,
colorBrightness: colorBrightness,
child: child
);
}
}
......@@ -79,7 +79,7 @@ class _SnackBarActionState extends State<SnackBarAction> {
margin: const EdgeInsets.only(left: _kSideMargins),
child: new FlatButton(
onPressed: _haveTriggeredAction ? null : _handlePressed,
textTheme: ButtonColor.accent,
textTheme: ButtonTextTheme.accent,
child: new Text(config.label)
)
);
......
......@@ -842,7 +842,7 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
Color indicatorColor = themeData.indicatorColor;
if (indicatorColor == backgroundColor) {
// ThemeData tries to avoid this by having indicatorColor avoid being the
// primaryColor. However, it's possible that the tab strip is on a
// primaryColor. However, it's possible that the tab bar is on a
// Material that isn't the primaryColor. In that case, if the indicator
// color ends up clashing, then this overrides it. When that happens,
// automatic transitions of the theme will likely look ugly as the
......
......@@ -282,7 +282,7 @@ class ThemeData {
/// remaining part of a progress bar.
final Color backgroundColor;
/// The color of the selected tab indicator in a tab strip.
/// The color of the selected tab indicator in a tab bar.
final Color indicatorColor;
/// The color to use for hint text or placeholder text, e.g. in
......
......@@ -104,7 +104,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
addAll(children);
}
/// The direction to use as the main axis
/// The direction to use as the main axis.
FlexDirection get direction => _direction;
FlexDirection _direction;
void set direction (FlexDirection value) {
......@@ -114,7 +114,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
}
}
/// How the children should be placed along the main axis
/// How the children should be placed along the main axis.
MainAxisAlignment get mainAxisAlignment => _mainAxisAlignment;
MainAxisAlignment _mainAxisAlignment;
void set mainAxisAlignment (MainAxisAlignment value) {
......@@ -124,7 +124,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
}
}
/// How the children should be placed along the cross axis
/// How the children should be placed along the cross axis.
CrossAxisAlignment get crossAxisAlignment => _crossAxisAlignment;
CrossAxisAlignment _crossAxisAlignment;
void set crossAxisAlignment (CrossAxisAlignment value) {
......@@ -134,7 +134,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
}
}
/// If using aligning items according to their baseline, which baseline to use
/// If aligning items according to their baseline, which baseline to use.
TextBaseline get textBaseline => _textBaseline;
TextBaseline _textBaseline;
void set textBaseline (TextBaseline value) {
......@@ -144,7 +144,7 @@ class RenderFlex extends RenderBox with ContainerRenderObjectMixin<RenderBox, Fl
}
}
/// Set during layout if overflow occurred on the main axis
/// Set during layout if overflow occurred on the main axis.
double _overflow;
@override
......
......@@ -1667,9 +1667,16 @@ class Flex extends MultiChildRenderObjectWidget {
assert(crossAxisAlignment != null);
}
/// The direction to use as the main axis.
final FlexDirection direction;
/// How the children should be placed along the main axis.
final MainAxisAlignment mainAxisAlignment;
/// How the children should be placed along the cross axis.
final CrossAxisAlignment crossAxisAlignment;
/// If aligning items according to their baseline, which baseline to use.
final TextBaseline textBaseline;
@override
......
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