Unverified Commit 9dce19e9 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Replace ButtonBar.bar method with ButtonBarTheme (#37544)

* Added new ButtonBarTheme to replace the deprecated ButtonTheme.bar method.

* Responding to PR feedback.

* [Material] Create material Banner component (#36880)

This PR creates a new material widget for the Banner component. This includes a theme as well. This widget can be dropped into any application, ideally at the top of a listview or scrollview.

(cherry picked from commit 35b6d668)

Removed the use of ButtonTheme.bar in the Banner implementation.

* Updated documentation from PR review comments.
parent ae291745
...@@ -324,22 +324,20 @@ class TravelDestinationContent extends StatelessWidget { ...@@ -324,22 +324,20 @@ class TravelDestinationContent extends StatelessWidget {
if (destination.type == CardDemoType.standard) { if (destination.type == CardDemoType.standard) {
children.add( children.add(
// share, explore buttons // share, explore buttons
ButtonTheme.bar( ButtonBar(
child: ButtonBar( alignment: MainAxisAlignment.start,
alignment: MainAxisAlignment.start, children: <Widget>[
children: <Widget>[ FlatButton(
FlatButton( child: Text('SHARE', semanticsLabel: 'Share ${destination.title}'),
child: Text('SHARE', semanticsLabel: 'Share ${destination.title}'), textColor: Colors.amber.shade500,
textColor: Colors.amber.shade500, onPressed: () { print('pressed'); },
onPressed: () { print('pressed'); }, ),
), FlatButton(
FlatButton( child: Text('EXPLORE', semanticsLabel: 'Explore ${destination.title}'),
child: Text('EXPLORE', semanticsLabel: 'Explore ${destination.title}'), textColor: Colors.amber.shade500,
textColor: Colors.amber.shade500, onPressed: () { print('pressed'); },
onPressed: () { print('pressed'); }, ),
), ],
],
),
), ),
); );
} }
......
...@@ -32,6 +32,7 @@ export 'src/material/bottom_sheet.dart'; ...@@ -32,6 +32,7 @@ export 'src/material/bottom_sheet.dart';
export 'src/material/bottom_sheet_theme.dart'; export 'src/material/bottom_sheet_theme.dart';
export 'src/material/button.dart'; export 'src/material/button.dart';
export 'src/material/button_bar.dart'; export 'src/material/button_bar.dart';
export 'src/material/button_bar_theme.dart';
export 'src/material/button_theme.dart'; export 'src/material/button_theme.dart';
export 'src/material/card.dart'; export 'src/material/card.dart';
export 'src/material/card_theme.dart'; export 'src/material/card_theme.dart';
......
...@@ -118,11 +118,9 @@ class MaterialBanner extends StatelessWidget { ...@@ -118,11 +118,9 @@ class MaterialBanner extends StatelessWidget {
?? bannerTheme.padding ?? bannerTheme.padding
?? const EdgeInsetsDirectional.only(end: 16.0); ?? const EdgeInsetsDirectional.only(end: 16.0);
final Widget buttonBar = ButtonTheme.bar( final Widget buttonBar = ButtonBar(
layoutBehavior: ButtonBarLayoutBehavior.constrained, layoutBehavior: ButtonBarLayoutBehavior.constrained,
child: ButtonBar( children: actions,
children: actions,
),
); );
final Color backgroundColor = this.backgroundColor final Color backgroundColor = this.backgroundColor
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_bar_theme.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'dialog.dart'; import 'dialog.dart';
import 'flat_button.dart'; import 'flat_button.dart';
...@@ -11,12 +12,24 @@ import 'raised_button.dart'; ...@@ -11,12 +12,24 @@ import 'raised_button.dart';
/// An end-aligned row of buttons. /// An end-aligned row of buttons.
/// ///
/// Places the buttons horizontally according to the padding in the current /// Places the buttons horizontally according to the [buttonPadding]. The
/// [ButtonTheme]. The children are laid out in a [Row] with /// children are laid out in a [Row] with [MainAxisAlignment.end]. When the
/// [MainAxisAlignment.end]. When the [Directionality] is [TextDirection.ltr], /// [Directionality] is [TextDirection.ltr], the button bar's children are
/// the button bar's children are right justified and the last child becomes /// right justified and the last child becomes the rightmost child. When the
/// the rightmost child. When the [Directionality] [TextDirection.rtl] the /// [Directionality] [TextDirection.rtl] the children are left justified and
/// children are left justified and the last child becomes the leftmost child. /// the last child becomes the leftmost child.
///
/// The [ButtonBar] can be configured with a [ButtonBarTheme]. For any null
/// property on the ButtonBar, the surrounding ButtonBarTheme's property
/// will be used instead. If the ButtonBarTheme's property is null
/// as well, the property will default to a value described in the field
/// documentation below.
///
/// The [children] are wrapped in a [ButtonTheme] that is a copy of the
/// surrounding ButtonTheme with the button properties overridden by the
/// properties of the ButtonBar as described above. These properties include
/// [buttonTextTheme], [buttonMinWidth], [buttonHeight], [buttonPadding],
/// and [buttonAlignedDropdown].
/// ///
/// Used by [Dialog] to arrange the actions at the bottom of the dialog. /// Used by [Dialog] to arrange the actions at the bottom of the dialog.
/// ///
...@@ -26,24 +39,84 @@ import 'raised_button.dart'; ...@@ -26,24 +39,84 @@ import 'raised_button.dart';
/// * [FlatButton], another kind of button. /// * [FlatButton], another kind of button.
/// * [Card], at the bottom of which it is common to place a [ButtonBar]. /// * [Card], at the bottom of which it is common to place a [ButtonBar].
/// * [Dialog], which uses a [ButtonBar] for its actions. /// * [Dialog], which uses a [ButtonBar] for its actions.
/// * [ButtonTheme], which configures the [ButtonBar]. /// * [ButtonBarTheme], which configures the [ButtonBar].
class ButtonBar extends StatelessWidget { class ButtonBar extends StatelessWidget {
/// Creates a button bar. /// Creates a button bar.
/// ///
/// The alignment argument defaults to [MainAxisAlignment.end]. /// Both [buttonMinWidth] and [buttonHeight] must be non-negative if they
/// are not null.
const ButtonBar({ const ButtonBar({
Key key, Key key,
this.alignment = MainAxisAlignment.end, this.alignment,
this.mainAxisSize = MainAxisSize.max, this.mainAxisSize,
this.buttonTextTheme,
this.buttonMinWidth,
this.buttonHeight,
this.buttonPadding,
this.buttonAlignedDropdown,
this.layoutBehavior,
this.children = const <Widget>[], this.children = const <Widget>[],
}) : super(key: key); }) : assert(buttonMinWidth == null || buttonMinWidth >= 0.0),
assert(buttonHeight == null || buttonHeight >= 0.0),
super(key: key);
/// How the children should be placed along the horizontal axis. /// How the children should be placed along the horizontal axis.
///
/// If null then it will use [ButtonBarTheme.alignment]. If that is null,
/// it will default to [MainAxisAlignment.end].
final MainAxisAlignment alignment; final MainAxisAlignment alignment;
/// How much horizontal space is available. See [Row.mainAxisSize]. /// How much horizontal space is available. See [Row.mainAxisSize].
///
/// If null then it will use the surrounding [ButtonBarTheme.mainAxisSize].
/// If that is null, it will default to [MainAxisSize.max].
final MainAxisSize mainAxisSize; final MainAxisSize mainAxisSize;
/// Overrides the surrounding [ButtonTheme.textTheme] to define a button's
/// base colors, size, internal padding and shape.
///
/// If null then it will use the surrounding [ButtonBarTheme.buttonTextTheme].
/// If that is null, it will default to [ButtonTextTheme.primary].
final ButtonTextTheme buttonTextTheme;
/// Overrides the surrounding [ButtonThemeData.minWidth] to define a button's
/// minimum width.
///
/// If null then it will use the surrounding [ButtonBarTheme.buttonMinWidth].
/// If that is null, it will default to 64.0 logical pixels.
final double buttonMinWidth;
/// Overrides the surrounding [ButtonThemeData.height] to define a button's
/// minimum height.
///
/// If null then it will use the surrounding [ButtonBarTheme.buttonHeight].
/// If that is null, it will default to 36.0 logical pixels.
final double buttonHeight;
/// Overrides the surrounding [ButtonThemeData.padding] to define the padding
/// for a button's child (typically the button's label).
///
/// If null then it will use the surrounding [ButtonBarTheme.buttonPadding].
/// If that is null, it will default to 8.0 logical pixels on the left
/// and right.
final EdgeInsetsGeometry buttonPadding;
/// Overrides the surrounding [ButtonThemeData.alignedDropdown] to define whether
/// a [DropdownButton] menu's width will match the button's width.
///
/// If null then it will use the surrounding [ButtonBarTheme.buttonAlignedDropdown].
/// If that is null, it will default to false.
final bool buttonAlignedDropdown;
/// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or with padding.
///
/// Overrides the surrounding [ButtonThemeData.layoutBehavior].
///
/// If null then it will use the surrounding [ButtonBarTheme.layoutBehavior].
/// If that is null, it will default [ButtonBarLayoutBehavior.padded].
final ButtonBarLayoutBehavior layoutBehavior;
/// The buttons to arrange horizontally. /// The buttons to arrange horizontally.
/// ///
/// Typically [RaisedButton] or [FlatButton] widgets. /// Typically [RaisedButton] or [FlatButton] widgets.
...@@ -51,18 +124,32 @@ class ButtonBar extends StatelessWidget { ...@@ -51,18 +124,32 @@ class ButtonBar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ButtonThemeData buttonTheme = ButtonTheme.of(context); final ButtonThemeData parentButtonTheme = ButtonTheme.of(context);
final ButtonBarThemeData barTheme = ButtonBarTheme.of(context);
final ButtonThemeData buttonTheme = parentButtonTheme.copyWith(
textTheme: buttonTextTheme ?? barTheme.buttonTextTheme ?? ButtonTextTheme.primary,
minWidth: buttonMinWidth ?? barTheme.buttonMinWidth ?? 64.0,
height: buttonHeight ?? barTheme.buttonHeight ?? 36.0,
padding: buttonPadding ?? barTheme.buttonPadding ?? const EdgeInsets.symmetric(horizontal: 8.0),
alignedDropdown: buttonAlignedDropdown ?? barTheme.buttonAlignedDropdown ?? false,
layoutBehavior: layoutBehavior ?? barTheme.layoutBehavior ?? ButtonBarLayoutBehavior.padded,
);
// We divide by 4.0 because we want half of the average of the left and right padding. // We divide by 4.0 because we want half of the average of the left and right padding.
final double paddingUnit = buttonTheme.padding.horizontal / 4.0; final double paddingUnit = buttonTheme.padding.horizontal / 4.0;
final Widget child = Row( final Widget child = ButtonTheme.fromButtonThemeData(
mainAxisAlignment: alignment, data: buttonTheme,
mainAxisSize: mainAxisSize, child: Row(
children: children.map<Widget>((Widget child) { mainAxisAlignment: alignment ?? barTheme.alignment ?? MainAxisAlignment.end,
return Padding( mainAxisSize: mainAxisSize ?? barTheme.mainAxisSize ?? MainAxisSize.max,
padding: EdgeInsets.symmetric(horizontal: paddingUnit), children: children.map<Widget>((Widget child) {
child: child, return Padding(
); padding: EdgeInsets.symmetric(horizontal: paddingUnit),
}).toList(), child: child,
);
}).toList(),
),
); );
switch (buttonTheme.layoutBehavior) { switch (buttonTheme.layoutBehavior) {
case ButtonBarLayoutBehavior.padded: case ButtonBarLayoutBehavior.padded:
......
// Copyright 2019 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 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'button_theme.dart';
import 'theme.dart';
/// Defines the visual properties of [ButtonBar] widgets.
///
/// Used by [ButtonBarTheme] to control the visual properties of [ButtonBar]
/// instances in a widget subtree.
///
/// To obtain this configuration, use [ButtonBarTheme.of] to access the closest
/// ancestor [ButtonBarTheme] of the current [BuildContext].
///
/// See also:
///
/// * [ButtonBarTheme], an [InheritedWidget] that propagates the theme down
/// its subtree.
/// * [ButtonBar], which uses this to configure itself and its children
/// button widgets.
class ButtonBarThemeData extends Diagnosticable {
/// Constructs the set of properties used to configure [ButtonBar] widgets.
///
/// Both [buttonMinWidth] and [buttonHeight] must be non-negative if they
/// are not null.
const ButtonBarThemeData({
this.alignment,
this.mainAxisSize,
this.buttonTextTheme,
this.buttonMinWidth,
this.buttonHeight,
this.buttonPadding,
this.buttonAlignedDropdown,
this.layoutBehavior,
}) : assert(buttonMinWidth == null || buttonMinWidth >= 0.0),
assert(buttonHeight == null || buttonHeight >= 0.0);
/// How the children should be placed along the horizontal axis.
final MainAxisAlignment alignment;
/// How much horizontal space is available. See [Row.mainAxisSize].
final MainAxisSize mainAxisSize;
/// Defines a [ButtonBar] button's base colors, and the defaults for
/// the button's minimum size, internal padding, and shape.
///
/// This will override the surrounding [ButtonTheme.textTheme] setting
/// for buttons contained in the [ButtonBar].
///
/// Despite the name, this property is not a [TextTheme], its value is not a
/// collection of [TextStyle]s.
final ButtonTextTheme buttonTextTheme;
/// The minimum width for [ButtonBar] buttons.
///
/// This will override the surrounding [ButtonTheme.minWidth] setting
/// for buttons contained in the [ButtonBar].
///
/// The actual horizontal space allocated for a button's child is
/// at least this value less the theme's horizontal [padding].
final double buttonMinWidth;
/// The minimum height for [ButtonBar] buttons.
///
/// This will override the surrounding [ButtonTheme.height] setting
/// for buttons contained in the [ButtonBar].
final double buttonHeight;
/// Padding for a [ButtonBar] button's child (typically the button's label).
///
/// This will override the surrounding [ButtonTheme.padding] setting
/// for buttons contained in the [ButtonBar].
final EdgeInsetsGeometry buttonPadding;
/// If true, then a [DropdownButton] menu's width will match the [ButtonBar]
/// button's width.
///
/// If false, then the dropdown's menu will be wider than
/// its button. In either case the dropdown button will line up the leading
/// edge of the menu's value with the leading edge of the values
/// displayed by the menu items.
///
/// This will override the surrounding [ButtonTheme.alignedDropdown] setting
/// for buttons contained in the [ButtonBar].
///
/// This property only affects [DropdownButton] contained in a [ButtonBar]
/// and its menu.
final bool buttonAlignedDropdown;
/// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or with padding.
final ButtonBarLayoutBehavior layoutBehavior;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
ButtonBarThemeData copyWith({
MainAxisAlignment alignment,
MainAxisSize mainAxisSize,
ButtonTextTheme buttonTextTheme,
double buttonMinWidth,
double buttonHeight,
EdgeInsetsGeometry buttonPadding,
bool buttonAlignedDropdown,
ButtonBarLayoutBehavior layoutBehavior,
}) {
return ButtonBarThemeData(
alignment: alignment ?? this.alignment,
mainAxisSize: mainAxisSize ?? this.mainAxisSize,
buttonTextTheme: buttonTextTheme ?? this.buttonTextTheme,
buttonMinWidth: buttonMinWidth ?? this.buttonMinWidth,
buttonHeight: buttonHeight ?? this.buttonHeight,
buttonPadding: buttonPadding ?? this.buttonPadding,
buttonAlignedDropdown: buttonAlignedDropdown ?? this.buttonAlignedDropdown,
layoutBehavior: layoutBehavior ?? this.layoutBehavior,
);
}
/// Linearly interpolate between two button bar themes.
///
/// If both arguments are null, then null is returned.
///
/// {@macro dart.ui.shadow.lerp}
static ButtonBarThemeData lerp(ButtonBarThemeData a, ButtonBarThemeData b, double t) {
assert(t != null);
if (a == null && b == null)
return null;
return ButtonBarThemeData(
alignment: t < 0.5 ? a.alignment : b.alignment,
mainAxisSize: t < 0.5 ? a.mainAxisSize : b.mainAxisSize,
buttonTextTheme: t < 0.5 ? a.buttonTextTheme : b.buttonTextTheme,
buttonMinWidth: lerpDouble(a?.buttonMinWidth, b?.buttonMinWidth, t),
buttonHeight: lerpDouble(a?.buttonHeight, b?.buttonHeight, t),
buttonPadding: EdgeInsets.lerp(a?.buttonPadding, b?.buttonPadding, t),
buttonAlignedDropdown: t < 0.5 ? a.buttonAlignedDropdown : b.buttonAlignedDropdown,
layoutBehavior: t < 0.5 ? a.layoutBehavior : b.layoutBehavior,
);
}
@override
int get hashCode {
return hashValues(
alignment,
mainAxisSize,
buttonTextTheme,
buttonMinWidth,
buttonHeight,
buttonPadding,
buttonAlignedDropdown,
layoutBehavior,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final ButtonBarThemeData typedOther = other;
return typedOther.alignment == alignment
&& typedOther.mainAxisSize == mainAxisSize
&& typedOther.buttonTextTheme == buttonTextTheme
&& typedOther.buttonMinWidth == buttonMinWidth
&& typedOther.buttonHeight == buttonHeight
&& typedOther.buttonPadding == buttonPadding
&& typedOther.buttonAlignedDropdown == buttonAlignedDropdown
&& typedOther.layoutBehavior == layoutBehavior;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<MainAxisAlignment>('alignment', alignment, defaultValue: null));
properties.add(DiagnosticsProperty<MainAxisSize>('mainAxisSize', mainAxisSize, defaultValue: null));
properties.add(DiagnosticsProperty<ButtonTextTheme>('textTheme', buttonTextTheme, defaultValue: null));
properties.add(DoubleProperty('minWidth', buttonMinWidth, defaultValue: null));
properties.add(DoubleProperty('height', buttonHeight, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', buttonPadding, defaultValue: null));
properties.add(FlagProperty(
'buttonAlignedDropdown',
value: buttonAlignedDropdown,
ifTrue: 'dropdown width matches button',
defaultValue: null));
properties.add(DiagnosticsProperty<ButtonBarLayoutBehavior>('layoutBehavior', layoutBehavior, defaultValue: null));
}
}
/// Applies a button bar theme to descendant [ButtonBar] widgets.
///
/// A button bar theme describes the layout and properties for the buttons
/// contained in a [ButtonBar].
///
/// Descendant widgets obtain the current theme's [ButtonBarTheme] object using
/// [ButtonBarTheme.of]. When a widget uses [ButtonBarTheme.of], it is automatically
/// rebuilt if the theme later changes.
///
/// A button bar theme can be specified as part of the overall Material theme
/// using [ThemeData.buttonBarTheme].
///
/// See also:
///
/// * [ButtonBarThemeData], which describes the actual configuration of a button
/// bar theme.
class ButtonBarTheme extends InheritedWidget {
/// Constructs a button bar theme that configures all descendent [ButtonBar]
/// widgets.
///
/// The [data] must not be null.
const ButtonBarTheme({
Key key,
@required this.data,
Widget child,
}) : assert(data != null), super(key: key, child: child);
/// The properties used for all descendant [ButtonBar] widgets.
final ButtonBarThemeData data;
/// Returns the configuration [data] from the closest [ButtonBarTheme]
/// ancestor. If there is no ancestor, it returns [ThemeData.buttonBarTheme].
/// Applications can assume that the returned value will not be null.
///
/// Typical usage is as follows:
///
/// ```dart
/// ButtonBarThemeData theme = ButtonBarTheme.of(context);
/// ```
static ButtonBarThemeData of(BuildContext context) {
final ButtonBarTheme buttonBarTheme = context.inheritFromWidgetOfExactType(ButtonBarTheme);
return buttonBarTheme?.data ?? Theme.of(context).buttonBarTheme;
}
@override
bool updateShouldNotify(ButtonBarTheme oldWidget) => data != oldWidget.data;
}
...@@ -120,20 +120,51 @@ class ButtonTheme extends InheritedWidget { ...@@ -120,20 +120,51 @@ class ButtonTheme extends InheritedWidget {
}) : assert(data != null), }) : assert(data != null),
super(key: key, child: child); super(key: key, child: child);
// TODO(darrenaustin): remove after this deprecation warning has been on
// stable for a couple of releases.
// See https://github.com/flutter/flutter/issues/37333
//
/// Creates a button theme that is appropriate for button bars, as used in /// Creates a button theme that is appropriate for button bars, as used in
/// dialog footers and in the headers of data tables. /// dialog footers and in the headers of data tables.
/// ///
/// This theme is denser, with a smaller [minWidth] and [padding], than the /// Deprecated. Please use [ButtonBarTheme] instead which offers more
/// default theme. Also, this theme uses [ButtonTextTheme.accent] rather than /// flexibility to configure [ButtonBar] widgets.
/// [ButtonTextTheme.normal]. ///
/// To migrate instances of code that were just wrapping a [ButtonBar]:
///
/// ```dart
/// ButtonTheme.bar(
/// child: ButtonBar(...)
/// );
/// ```
///
/// you can just remove the `ButtonTheme.bar` as the defaults are now handled
/// by [ButtonBar] directly.
///
/// If you have more complicated usages of `ButtonTheme.bar` like:
/// ///
/// For best effect, the label of the button at the edge of the container /// ```dart
/// should have text that ends up wider than 64.0 pixels. This ensures that /// ButtonTheme.bar(
/// the alignment of the text matches the alignment of the edge of the /// padding: EdgeInsets.symmetric(horizontal: 10.0),
/// container. /// textTheme: ButtonTextTheme.accent,
/// child: ButtonBar(...),
/// );
/// ```
///
/// you can remove the `ButtonTheme.bar` and move the parameters to the
/// [ButtonBar] instance directly:
///
/// ```dart
/// ButtonBar(
/// padding: EdgeInsets.symmetric(horizontal: 10.0),
/// textTheme: ButtonTextTheme.accent,
/// ...
/// );
/// ```
/// ///
/// For example, buttons at the bottom of [Dialog] or [Card] widgets use this /// You can also replace the defaults for all [ButtonBar] widgets by updating
/// button theme. /// [ThemeData.buttonBarTheme] for your app.
@Deprecated('use ButtonBarTheme instead')
ButtonTheme.bar({ ButtonTheme.bar({
Key key, Key key,
ButtonTextTheme textTheme = ButtonTextTheme.accent, ButtonTextTheme textTheme = ButtonTextTheme.accent,
......
...@@ -36,19 +36,17 @@ import 'theme.dart'; ...@@ -36,19 +36,17 @@ import 'theme.dart';
/// title: Text('The Enchanted Nightingale'), /// title: Text('The Enchanted Nightingale'),
/// subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'), /// subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
/// ), /// ),
/// ButtonTheme.bar( // make buttons use the appropriate styles for cards /// ButtonBar(
/// child: ButtonBar( /// children: <Widget>[
/// children: <Widget>[ /// FlatButton(
/// FlatButton( /// child: const Text('BUY TICKETS'),
/// child: const Text('BUY TICKETS'), /// onPressed: () { /* ... */ },
/// onPressed: () { /* ... */ }, /// ),
/// ), /// FlatButton(
/// FlatButton( /// child: const Text('LISTEN'),
/// child: const Text('LISTEN'), /// onPressed: () { /* ... */ },
/// onPressed: () { /* ... */ }, /// ),
/// ), /// ],
/// ],
/// ),
/// ), /// ),
/// ], /// ],
/// ), /// ),
...@@ -92,8 +90,7 @@ import 'theme.dart'; ...@@ -92,8 +90,7 @@ import 'theme.dart';
/// See also: /// See also:
/// ///
/// * [ListTile], to display icons and text in a card. /// * [ListTile], to display icons and text in a card.
/// * [ButtonBar], to display buttons at the bottom of a card. Typically these /// * [ButtonBar], to display buttons at the bottom of a card.
/// would be styled using a [ButtonTheme] created with [new ButtonTheme.bar].
/// * [showDialog], to display a modal card. /// * [showDialog], to display a modal card.
/// * <https://material.io/design/components/cards.html> /// * <https://material.io/design/components/cards.html>
class Card extends StatelessWidget { class Card extends StatelessWidget {
......
...@@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart'; ...@@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'button_bar.dart'; import 'button_bar.dart';
import 'button_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'dialog.dart'; import 'dialog.dart';
...@@ -986,19 +985,17 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { ...@@ -986,19 +985,17 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final Widget picker = _buildPicker(); final Widget picker = _buildPicker();
final Widget actions = ButtonTheme.bar( final Widget actions = ButtonBar(
child: ButtonBar( children: <Widget>[
children: <Widget>[ FlatButton(
FlatButton( child: Text(localizations.cancelButtonLabel),
child: Text(localizations.cancelButtonLabel), onPressed: _handleCancel,
onPressed: _handleCancel, ),
), FlatButton(
FlatButton( child: Text(localizations.okButtonLabel),
child: Text(localizations.okButtonLabel), onPressed: _handleOk,
onPressed: _handleOk, ),
), ],
],
),
); );
final Dialog dialog = Dialog( final Dialog dialog = Dialog(
......
...@@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart'; ...@@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_bar.dart'; import 'button_bar.dart';
import 'button_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'dialog_theme.dart'; import 'dialog_theme.dart';
...@@ -343,10 +342,8 @@ class AlertDialog extends StatelessWidget { ...@@ -343,10 +342,8 @@ class AlertDialog extends StatelessWidget {
} }
if (actions != null) { if (actions != null) {
children.add(ButtonTheme.bar( children.add(ButtonBar(
child: ButtonBar( children: actions,
children: actions,
),
)); ));
} }
......
...@@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart'; ...@@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior; import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'button_bar.dart'; import 'button_bar.dart';
import 'button_theme.dart';
import 'card.dart'; import 'card.dart';
import 'constants.dart'; import 'constants.dart';
import 'data_table.dart'; import 'data_table.dart';
...@@ -439,16 +438,14 @@ class PaginatedDataTableState extends State<PaginatedDataTable> { ...@@ -439,16 +438,14 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
data: const IconThemeData( data: const IconThemeData(
opacity: 0.54 opacity: 0.54
), ),
child: ButtonTheme.bar( child: Ink(
child: Ink( height: 64.0,
height: 64.0, color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null,
color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null, child: Padding(
child: Padding( padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0),
padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0), child: Row(
child: Row( mainAxisAlignment: MainAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end, children: headerWidgets,
children: headerWidgets,
),
), ),
), ),
), ),
......
...@@ -14,7 +14,6 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; ...@@ -14,7 +14,6 @@ import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'app_bar.dart'; import 'app_bar.dart';
import 'bottom_sheet.dart'; import 'bottom_sheet.dart';
import 'button_bar.dart'; import 'button_bar.dart';
import 'button_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'divider.dart'; import 'divider.dart';
import 'drawer.dart'; import 'drawer.dart';
...@@ -2154,13 +2153,9 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin { ...@@ -2154,13 +2153,9 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
), ),
), ),
child: SafeArea( child: SafeArea(
child: ButtonTheme.bar( top: false,
child: SafeArea( child: ButtonBar(
top: false, children: widget.persistentFooterButtons,
child: ButtonBar(
children: widget.persistentFooterButtons,
),
),
), ),
), ),
), ),
......
...@@ -301,9 +301,10 @@ class SnackBar extends StatelessWidget { ...@@ -301,9 +301,10 @@ class SnackBar extends StatelessWidget {
), ),
]; ];
if (action != null) { if (action != null) {
children.add(ButtonTheme.bar( children.add(ButtonTheme(
padding: EdgeInsets.symmetric(horizontal: snackBarPadding),
textTheme: ButtonTextTheme.accent, textTheme: ButtonTextTheme.accent,
minWidth: 64.0,
padding: EdgeInsets.symmetric(horizontal: snackBarPadding),
child: action, child: action,
)); ));
} else { } else {
......
...@@ -13,6 +13,7 @@ import 'app_bar_theme.dart'; ...@@ -13,6 +13,7 @@ import 'app_bar_theme.dart';
import 'banner_theme.dart'; import 'banner_theme.dart';
import 'bottom_app_bar_theme.dart'; import 'bottom_app_bar_theme.dart';
import 'bottom_sheet_theme.dart'; import 'bottom_sheet_theme.dart';
import 'button_bar_theme.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'card_theme.dart'; import 'card_theme.dart';
import 'chip_theme.dart'; import 'chip_theme.dart';
...@@ -180,6 +181,7 @@ class ThemeData extends Diagnosticable { ...@@ -180,6 +181,7 @@ class ThemeData extends Diagnosticable {
PopupMenuThemeData popupMenuTheme, PopupMenuThemeData popupMenuTheme,
MaterialBannerThemeData bannerTheme, MaterialBannerThemeData bannerTheme,
DividerThemeData dividerTheme, DividerThemeData dividerTheme,
ButtonBarThemeData buttonBarTheme,
}) { }) {
brightness ??= Brightness.light; brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark; final bool isDark = brightness == Brightness.dark;
...@@ -285,6 +287,7 @@ class ThemeData extends Diagnosticable { ...@@ -285,6 +287,7 @@ class ThemeData extends Diagnosticable {
popupMenuTheme ??= const PopupMenuThemeData(); popupMenuTheme ??= const PopupMenuThemeData();
bannerTheme ??= const MaterialBannerThemeData(); bannerTheme ??= const MaterialBannerThemeData();
dividerTheme ??= const DividerThemeData(); dividerTheme ??= const DividerThemeData();
buttonBarTheme ??= const ButtonBarThemeData();
return ThemeData.raw( return ThemeData.raw(
brightness: brightness, brightness: brightness,
...@@ -348,6 +351,7 @@ class ThemeData extends Diagnosticable { ...@@ -348,6 +351,7 @@ class ThemeData extends Diagnosticable {
popupMenuTheme: popupMenuTheme, popupMenuTheme: popupMenuTheme,
bannerTheme: bannerTheme, bannerTheme: bannerTheme,
dividerTheme: dividerTheme, dividerTheme: dividerTheme,
buttonBarTheme: buttonBarTheme,
); );
} }
...@@ -423,6 +427,7 @@ class ThemeData extends Diagnosticable { ...@@ -423,6 +427,7 @@ class ThemeData extends Diagnosticable {
@required this.popupMenuTheme, @required this.popupMenuTheme,
@required this.bannerTheme, @required this.bannerTheme,
@required this.dividerTheme, @required this.dividerTheme,
@required this.buttonBarTheme,
}) : assert(brightness != null), }) : assert(brightness != null),
assert(primaryColor != null), assert(primaryColor != null),
assert(primaryColorBrightness != null), assert(primaryColorBrightness != null),
...@@ -480,7 +485,8 @@ class ThemeData extends Diagnosticable { ...@@ -480,7 +485,8 @@ class ThemeData extends Diagnosticable {
assert(bottomSheetTheme != null), assert(bottomSheetTheme != null),
assert(popupMenuTheme != null), assert(popupMenuTheme != null),
assert(bannerTheme != null), assert(bannerTheme != null),
assert(dividerTheme != null); assert(dividerTheme != null),
assert(buttonBarTheme != null);
/// Create a [ThemeData] based on the colors in the given [colorScheme] and /// Create a [ThemeData] based on the colors in the given [colorScheme] and
/// text styles of the optional [textTheme]. /// text styles of the optional [textTheme].
...@@ -877,6 +883,9 @@ class ThemeData extends Diagnosticable { ...@@ -877,6 +883,9 @@ class ThemeData extends Diagnosticable {
/// [VerticalDivider]s, etc. /// [VerticalDivider]s, etc.
final DividerThemeData dividerTheme; final DividerThemeData dividerTheme;
/// A theme for customizing the appearance and layout of [ButtonBar] widgets.
final ButtonBarThemeData buttonBarTheme;
/// Creates a copy of this theme but with the given fields replaced with the new values. /// Creates a copy of this theme but with the given fields replaced with the new values.
ThemeData copyWith({ ThemeData copyWith({
Brightness brightness, Brightness brightness,
...@@ -940,6 +949,7 @@ class ThemeData extends Diagnosticable { ...@@ -940,6 +949,7 @@ class ThemeData extends Diagnosticable {
PopupMenuThemeData popupMenuTheme, PopupMenuThemeData popupMenuTheme,
MaterialBannerThemeData bannerTheme, MaterialBannerThemeData bannerTheme,
DividerThemeData dividerTheme, DividerThemeData dividerTheme,
ButtonBarThemeData buttonBarTheme,
}) { }) {
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault(); cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
return ThemeData.raw( return ThemeData.raw(
...@@ -1004,6 +1014,7 @@ class ThemeData extends Diagnosticable { ...@@ -1004,6 +1014,7 @@ class ThemeData extends Diagnosticable {
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme, popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
bannerTheme: bannerTheme ?? this.bannerTheme, bannerTheme: bannerTheme ?? this.bannerTheme,
dividerTheme: dividerTheme ?? this.dividerTheme, dividerTheme: dividerTheme ?? this.dividerTheme,
buttonBarTheme: buttonBarTheme ?? this.buttonBarTheme,
); );
} }
...@@ -1146,6 +1157,7 @@ class ThemeData extends Diagnosticable { ...@@ -1146,6 +1157,7 @@ class ThemeData extends Diagnosticable {
popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t), popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t),
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t), bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t), dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t),
buttonBarTheme: ButtonBarThemeData.lerp(a.buttonBarTheme, b.buttonBarTheme, t),
); );
} }
...@@ -1215,7 +1227,8 @@ class ThemeData extends Diagnosticable { ...@@ -1215,7 +1227,8 @@ class ThemeData extends Diagnosticable {
(otherData.bottomSheetTheme == bottomSheetTheme) && (otherData.bottomSheetTheme == bottomSheetTheme) &&
(otherData.popupMenuTheme == popupMenuTheme) && (otherData.popupMenuTheme == popupMenuTheme) &&
(otherData.bannerTheme == bannerTheme) && (otherData.bannerTheme == bannerTheme) &&
(otherData.dividerTheme == dividerTheme); (otherData.dividerTheme == dividerTheme) &&
(otherData.buttonBarTheme == buttonBarTheme);
} }
@override @override
...@@ -1285,6 +1298,7 @@ class ThemeData extends Diagnosticable { ...@@ -1285,6 +1298,7 @@ class ThemeData extends Diagnosticable {
popupMenuTheme, popupMenuTheme,
bannerTheme, bannerTheme,
dividerTheme, dividerTheme,
buttonBarTheme,
]; ];
return hashList(values); return hashList(values);
} }
...@@ -1351,6 +1365,7 @@ class ThemeData extends Diagnosticable { ...@@ -1351,6 +1365,7 @@ class ThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme)); properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme));
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme)); properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme));
properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme)); properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme));
properties.add(DiagnosticsProperty<ButtonBarThemeData>('buttonBarTheme', buttonBarTheme, defaultValue: defaultData.buttonBarTheme));
} }
} }
......
...@@ -10,7 +10,6 @@ import 'package:flutter/services.dart'; ...@@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_bar.dart'; import 'button_bar.dart';
import 'button_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'debug.dart'; import 'debug.dart';
import 'dialog.dart'; import 'dialog.dart';
...@@ -1613,19 +1612,17 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { ...@@ -1613,19 +1612,17 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
), ),
); );
final Widget actions = ButtonTheme.bar( final Widget actions = ButtonBar(
child: ButtonBar( children: <Widget>[
children: <Widget>[ FlatButton(
FlatButton( child: Text(localizations.cancelButtonLabel),
child: Text(localizations.cancelButtonLabel), onPressed: _handleCancel,
onPressed: _handleCancel, ),
), FlatButton(
FlatButton( child: Text(localizations.okButtonLabel),
child: Text(localizations.okButtonLabel), onPressed: _handleOk,
onPressed: _handleOk, ),
), ],
],
),
); );
final Dialog dialog = Dialog( final Dialog dialog = Dialog(
......
// Copyright 2019 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('ButtonBarThemeData null fields by default', () {
const ButtonBarThemeData buttonBarTheme = ButtonBarThemeData();
expect(buttonBarTheme.alignment, null);
expect(buttonBarTheme.mainAxisSize, null);
expect(buttonBarTheme.buttonTextTheme, null);
expect(buttonBarTheme.buttonMinWidth, null);
expect(buttonBarTheme.buttonHeight, null);
expect(buttonBarTheme.buttonPadding, null);
expect(buttonBarTheme.buttonAlignedDropdown, null);
expect(buttonBarTheme.layoutBehavior, null);
});
test('ThemeData uses default ButtonBarThemeData', () {
expect(ThemeData().buttonBarTheme, equals(const ButtonBarThemeData()));
});
test('ButtonBarThemeData copyWith, ==, hashCode basics', () {
expect(const ButtonBarThemeData(), const ButtonBarThemeData().copyWith());
expect(const ButtonBarThemeData().hashCode, const ButtonBarThemeData().copyWith().hashCode);
});
testWidgets('ButtonBarThemeData lerps correctly', (WidgetTester tester) async {
const ButtonBarThemeData barThemePrimary = ButtonBarThemeData(
alignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
buttonTextTheme: ButtonTextTheme.primary,
buttonMinWidth: 20.0,
buttonHeight: 20.0,
buttonPadding: EdgeInsets.symmetric(vertical: 5.0),
buttonAlignedDropdown: false,
layoutBehavior: ButtonBarLayoutBehavior.padded,
);
const ButtonBarThemeData barThemeAccent = ButtonBarThemeData(
alignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
buttonTextTheme: ButtonTextTheme.accent,
buttonMinWidth: 10.0,
buttonHeight: 40.0,
buttonPadding: EdgeInsets.symmetric(horizontal: 10.0),
buttonAlignedDropdown: true,
layoutBehavior: ButtonBarLayoutBehavior.constrained,
);
final ButtonBarThemeData lerp = ButtonBarThemeData.lerp(barThemePrimary, barThemeAccent, 0.5);
expect(lerp.alignment, equals(MainAxisAlignment.center));
expect(lerp.mainAxisSize, equals(MainAxisSize.max));
expect(lerp.buttonTextTheme, equals(ButtonTextTheme.accent));
expect(lerp.buttonMinWidth, equals(15.0));
expect(lerp.buttonHeight, equals(30.0));
expect(lerp.buttonPadding, equals(const EdgeInsets.fromLTRB(5.0, 2.5, 5.0, 2.5)));
expect(lerp.buttonAlignedDropdown, isTrue);
expect(lerp.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained));
});
testWidgets('Default ButtonBarThemeData debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const ButtonBarThemeData().debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
testWidgets('ButtonBarThemeData implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const ButtonBarThemeData(
alignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
buttonTextTheme: ButtonTextTheme.accent,
buttonMinWidth: 10.0,
buttonHeight: 42.0,
buttonPadding: EdgeInsets.symmetric(horizontal: 7.3),
buttonAlignedDropdown: true,
layoutBehavior: ButtonBarLayoutBehavior.constrained,
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[
'alignment: MainAxisAlignment.center',
'mainAxisSize: MainAxisSize.max',
'textTheme: ButtonTextTheme.accent',
'minWidth: 10.0',
'height: 42.0',
'padding: EdgeInsets(7.3, 0.0, 7.3, 0.0)',
'dropdown width matches button',
'layoutBehavior: ButtonBarLayoutBehavior.constrained',
]);
});
testWidgets('ButtonBarTheme.of falls back to ThemeData.buttonBarTheme', (WidgetTester tester) async {
const ButtonBarThemeData buttonBarTheme = ButtonBarThemeData(buttonMinWidth: 42.0);
BuildContext capturedContext;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(buttonBarTheme: buttonBarTheme),
home: Builder(
builder: (BuildContext context) {
capturedContext = context;
return Container();
}
)
)
);
expect(ButtonBarTheme.of(capturedContext), equals(buttonBarTheme));
expect(ButtonBarTheme.of(capturedContext).buttonMinWidth, equals(42.0));
});
testWidgets('ButtonBarTheme overrides ThemeData.buttonBarTheme', (WidgetTester tester) async {
const ButtonBarThemeData defaultBarTheme = ButtonBarThemeData(buttonMinWidth: 42.0);
const ButtonBarThemeData buttonBarTheme = ButtonBarThemeData(buttonMinWidth: 84.0);
BuildContext capturedContext;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(buttonBarTheme: defaultBarTheme),
home: Builder(
builder: (BuildContext context) {
return ButtonBarTheme(
data: buttonBarTheme,
child: Builder(
builder: (BuildContext context) {
capturedContext = context;
return Container();
},
),
);
}
)
)
);
expect(ButtonBarTheme.of(capturedContext), equals(buttonBarTheme));
expect(ButtonBarTheme.of(capturedContext).buttonMinWidth, equals(84.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