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 35b6d668e197035ea2b57adb99041542982e8be0)
Removed the use of ButtonTheme.bar in the Banner implementation.
* Updated documentation from PR review comments.
diff --git a/examples/flutter_gallery/lib/demo/material/cards_demo.dart b/examples/flutter_gallery/lib/demo/material/cards_demo.dart
index 4e3b788..39c1106 100644
--- a/examples/flutter_gallery/lib/demo/material/cards_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/cards_demo.dart
@@ -324,22 +324,20 @@
if (destination.type == CardDemoType.standard) {
children.add(
// share, explore buttons
- ButtonTheme.bar(
- child: ButtonBar(
- alignment: MainAxisAlignment.start,
- children: <Widget>[
- FlatButton(
- child: Text('SHARE', semanticsLabel: 'Share ${destination.title}'),
- textColor: Colors.amber.shade500,
- onPressed: () { print('pressed'); },
- ),
- FlatButton(
- child: Text('EXPLORE', semanticsLabel: 'Explore ${destination.title}'),
- textColor: Colors.amber.shade500,
- onPressed: () { print('pressed'); },
- ),
- ],
- ),
+ ButtonBar(
+ alignment: MainAxisAlignment.start,
+ children: <Widget>[
+ FlatButton(
+ child: Text('SHARE', semanticsLabel: 'Share ${destination.title}'),
+ textColor: Colors.amber.shade500,
+ onPressed: () { print('pressed'); },
+ ),
+ FlatButton(
+ child: Text('EXPLORE', semanticsLabel: 'Explore ${destination.title}'),
+ textColor: Colors.amber.shade500,
+ onPressed: () { print('pressed'); },
+ ),
+ ],
),
);
}
diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart
index e951962..076af21 100644
--- a/packages/flutter/lib/material.dart
+++ b/packages/flutter/lib/material.dart
@@ -32,6 +32,7 @@
export 'src/material/bottom_sheet_theme.dart';
export 'src/material/button.dart';
export 'src/material/button_bar.dart';
+export 'src/material/button_bar_theme.dart';
export 'src/material/button_theme.dart';
export 'src/material/card.dart';
export 'src/material/card_theme.dart';
diff --git a/packages/flutter/lib/src/material/banner.dart b/packages/flutter/lib/src/material/banner.dart
index bac6fd9..b3364c9 100644
--- a/packages/flutter/lib/src/material/banner.dart
+++ b/packages/flutter/lib/src/material/banner.dart
@@ -118,11 +118,9 @@
?? bannerTheme.padding
?? const EdgeInsetsDirectional.only(end: 16.0);
- final Widget buttonBar = ButtonTheme.bar(
+ final Widget buttonBar = ButtonBar(
layoutBehavior: ButtonBarLayoutBehavior.constrained,
- child: ButtonBar(
- children: actions,
- ),
+ children: actions,
);
final Color backgroundColor = this.backgroundColor
diff --git a/packages/flutter/lib/src/material/button_bar.dart b/packages/flutter/lib/src/material/button_bar.dart
index 2e4ed41..4e5298c 100644
--- a/packages/flutter/lib/src/material/button_bar.dart
+++ b/packages/flutter/lib/src/material/button_bar.dart
@@ -4,6 +4,7 @@
import 'package:flutter/widgets.dart';
+import 'button_bar_theme.dart';
import 'button_theme.dart';
import 'dialog.dart';
import 'flat_button.dart';
@@ -11,12 +12,24 @@
/// An end-aligned row of buttons.
///
-/// Places the buttons horizontally according to the padding in the current
-/// [ButtonTheme]. The children are laid out in a [Row] with
-/// [MainAxisAlignment.end]. When the [Directionality] is [TextDirection.ltr],
-/// the button bar's children are right justified and the last child becomes
-/// the rightmost child. When the [Directionality] [TextDirection.rtl] the
-/// children are left justified and the last child becomes the leftmost child.
+/// Places the buttons horizontally according to the [buttonPadding]. The
+/// children are laid out in a [Row] with [MainAxisAlignment.end]. When the
+/// [Directionality] is [TextDirection.ltr], the button bar's children are
+/// right justified and the last child becomes the rightmost child. When the
+/// [Directionality] [TextDirection.rtl] the children are left justified and
+/// 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.
///
@@ -26,24 +39,84 @@
/// * [FlatButton], another kind of button.
/// * [Card], at the bottom of which it is common to place a [ButtonBar].
/// * [Dialog], which uses a [ButtonBar] for its actions.
-/// * [ButtonTheme], which configures the [ButtonBar].
+/// * [ButtonBarTheme], which configures the [ButtonBar].
class ButtonBar extends StatelessWidget {
/// 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({
Key key,
- this.alignment = MainAxisAlignment.end,
- this.mainAxisSize = MainAxisSize.max,
+ this.alignment,
+ this.mainAxisSize,
+ this.buttonTextTheme,
+ this.buttonMinWidth,
+ this.buttonHeight,
+ this.buttonPadding,
+ this.buttonAlignedDropdown,
+ this.layoutBehavior,
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.
+ ///
+ /// If null then it will use [ButtonBarTheme.alignment]. If that is null,
+ /// it will default to [MainAxisAlignment.end].
final MainAxisAlignment alignment;
/// 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;
+ /// 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.
///
/// Typically [RaisedButton] or [FlatButton] widgets.
@@ -51,18 +124,32 @@
@override
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.
final double paddingUnit = buttonTheme.padding.horizontal / 4.0;
- final Widget child = Row(
- mainAxisAlignment: alignment,
- mainAxisSize: mainAxisSize,
- children: children.map<Widget>((Widget child) {
- return Padding(
- padding: EdgeInsets.symmetric(horizontal: paddingUnit),
- child: child,
- );
- }).toList(),
+ final Widget child = ButtonTheme.fromButtonThemeData(
+ data: buttonTheme,
+ child: Row(
+ mainAxisAlignment: alignment ?? barTheme.alignment ?? MainAxisAlignment.end,
+ mainAxisSize: mainAxisSize ?? barTheme.mainAxisSize ?? MainAxisSize.max,
+ children: children.map<Widget>((Widget child) {
+ return Padding(
+ padding: EdgeInsets.symmetric(horizontal: paddingUnit),
+ child: child,
+ );
+ }).toList(),
+ ),
);
switch (buttonTheme.layoutBehavior) {
case ButtonBarLayoutBehavior.padded:
diff --git a/packages/flutter/lib/src/material/button_bar_theme.dart b/packages/flutter/lib/src/material/button_bar_theme.dart
new file mode 100644
index 0000000..b2bc04a
--- /dev/null
+++ b/packages/flutter/lib/src/material/button_bar_theme.dart
@@ -0,0 +1,240 @@
+// 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;
+}
diff --git a/packages/flutter/lib/src/material/button_theme.dart b/packages/flutter/lib/src/material/button_theme.dart
index 2bd426f..67afdc4 100644
--- a/packages/flutter/lib/src/material/button_theme.dart
+++ b/packages/flutter/lib/src/material/button_theme.dart
@@ -120,20 +120,51 @@
}) : assert(data != null),
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
/// dialog footers and in the headers of data tables.
///
- /// This theme is denser, with a smaller [minWidth] and [padding], than the
- /// default theme. Also, this theme uses [ButtonTextTheme.accent] rather than
- /// [ButtonTextTheme.normal].
+ /// Deprecated. Please use [ButtonBarTheme] instead which offers more
+ /// flexibility to configure [ButtonBar] widgets.
///
- /// For best effect, the label of the button at the edge of the container
- /// should have text that ends up wider than 64.0 pixels. This ensures that
- /// the alignment of the text matches the alignment of the edge of the
- /// container.
+ /// To migrate instances of code that were just wrapping a [ButtonBar]:
///
- /// For example, buttons at the bottom of [Dialog] or [Card] widgets use this
- /// button theme.
+ /// ```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:
+ ///
+ /// ```dart
+ /// ButtonTheme.bar(
+ /// padding: EdgeInsets.symmetric(horizontal: 10.0),
+ /// 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,
+ /// ...
+ /// );
+ /// ```
+ ///
+ /// You can also replace the defaults for all [ButtonBar] widgets by updating
+ /// [ThemeData.buttonBarTheme] for your app.
+ @Deprecated('use ButtonBarTheme instead')
ButtonTheme.bar({
Key key,
ButtonTextTheme textTheme = ButtonTextTheme.accent,
diff --git a/packages/flutter/lib/src/material/card.dart b/packages/flutter/lib/src/material/card.dart
index a8c45b6..1ff6aa0 100644
--- a/packages/flutter/lib/src/material/card.dart
+++ b/packages/flutter/lib/src/material/card.dart
@@ -36,19 +36,17 @@
/// title: Text('The Enchanted Nightingale'),
/// subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
/// ),
-/// ButtonTheme.bar( // make buttons use the appropriate styles for cards
-/// child: ButtonBar(
-/// children: <Widget>[
-/// FlatButton(
-/// child: const Text('BUY TICKETS'),
-/// onPressed: () { /* ... */ },
-/// ),
-/// FlatButton(
-/// child: const Text('LISTEN'),
-/// onPressed: () { /* ... */ },
-/// ),
-/// ],
-/// ),
+/// ButtonBar(
+/// children: <Widget>[
+/// FlatButton(
+/// child: const Text('BUY TICKETS'),
+/// onPressed: () { /* ... */ },
+/// ),
+/// FlatButton(
+/// child: const Text('LISTEN'),
+/// onPressed: () { /* ... */ },
+/// ),
+/// ],
/// ),
/// ],
/// ),
@@ -92,8 +90,7 @@
/// See also:
///
/// * [ListTile], to display icons and text in a card.
-/// * [ButtonBar], to display buttons at the bottom of a card. Typically these
-/// would be styled using a [ButtonTheme] created with [new ButtonTheme.bar].
+/// * [ButtonBar], to display buttons at the bottom of a card.
/// * [showDialog], to display a modal card.
/// * <https://material.io/design/components/cards.html>
class Card extends StatelessWidget {
diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart
index 7e3e985..bead9d4 100644
--- a/packages/flutter/lib/src/material/date_picker.dart
+++ b/packages/flutter/lib/src/material/date_picker.dart
@@ -11,7 +11,6 @@
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'button_bar.dart';
-import 'button_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'dialog.dart';
@@ -986,19 +985,17 @@
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final Widget picker = _buildPicker();
- final Widget actions = ButtonTheme.bar(
- child: ButtonBar(
- children: <Widget>[
- FlatButton(
- child: Text(localizations.cancelButtonLabel),
- onPressed: _handleCancel,
- ),
- FlatButton(
- child: Text(localizations.okButtonLabel),
- onPressed: _handleOk,
- ),
- ],
- ),
+ final Widget actions = ButtonBar(
+ children: <Widget>[
+ FlatButton(
+ child: Text(localizations.cancelButtonLabel),
+ onPressed: _handleCancel,
+ ),
+ FlatButton(
+ child: Text(localizations.okButtonLabel),
+ onPressed: _handleOk,
+ ),
+ ],
);
final Dialog dialog = Dialog(
diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart
index 36f0209..167c0ab 100644
--- a/packages/flutter/lib/src/material/dialog.dart
+++ b/packages/flutter/lib/src/material/dialog.dart
@@ -8,7 +8,6 @@
import 'package:flutter/widgets.dart';
import 'button_bar.dart';
-import 'button_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'dialog_theme.dart';
@@ -343,10 +342,8 @@
}
if (actions != null) {
- children.add(ButtonTheme.bar(
- child: ButtonBar(
- children: actions,
- ),
+ children.add(ButtonBar(
+ children: actions,
));
}
diff --git a/packages/flutter/lib/src/material/paginated_data_table.dart b/packages/flutter/lib/src/material/paginated_data_table.dart
index 0627791..3451015 100644
--- a/packages/flutter/lib/src/material/paginated_data_table.dart
+++ b/packages/flutter/lib/src/material/paginated_data_table.dart
@@ -9,7 +9,6 @@
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'button_bar.dart';
-import 'button_theme.dart';
import 'card.dart';
import 'constants.dart';
import 'data_table.dart';
@@ -439,16 +438,14 @@
data: const IconThemeData(
opacity: 0.54
),
- child: ButtonTheme.bar(
- child: Ink(
- height: 64.0,
- color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null,
- child: Padding(
- padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: headerWidgets,
- ),
+ child: Ink(
+ height: 64.0,
+ color: _selectedRowCount > 0 ? themeData.secondaryHeaderColor : null,
+ child: Padding(
+ padding: EdgeInsetsDirectional.only(start: startPadding, end: 14.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: headerWidgets,
),
),
),
diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart
index d71d76a..4488d8e 100644
--- a/packages/flutter/lib/src/material/scaffold.dart
+++ b/packages/flutter/lib/src/material/scaffold.dart
@@ -14,7 +14,6 @@
import 'app_bar.dart';
import 'bottom_sheet.dart';
import 'button_bar.dart';
-import 'button_theme.dart';
import 'colors.dart';
import 'divider.dart';
import 'drawer.dart';
@@ -2154,13 +2153,9 @@
),
),
child: SafeArea(
- child: ButtonTheme.bar(
- child: SafeArea(
- top: false,
- child: ButtonBar(
- children: widget.persistentFooterButtons,
- ),
- ),
+ top: false,
+ child: ButtonBar(
+ children: widget.persistentFooterButtons,
),
),
),
diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart
index e5e6398..db70eec 100644
--- a/packages/flutter/lib/src/material/snack_bar.dart
+++ b/packages/flutter/lib/src/material/snack_bar.dart
@@ -301,9 +301,10 @@
),
];
if (action != null) {
- children.add(ButtonTheme.bar(
- padding: EdgeInsets.symmetric(horizontal: snackBarPadding),
+ children.add(ButtonTheme(
textTheme: ButtonTextTheme.accent,
+ minWidth: 64.0,
+ padding: EdgeInsets.symmetric(horizontal: snackBarPadding),
child: action,
));
} else {
diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart
index 981a387..dacad32 100644
--- a/packages/flutter/lib/src/material/theme_data.dart
+++ b/packages/flutter/lib/src/material/theme_data.dart
@@ -13,6 +13,7 @@
import 'banner_theme.dart';
import 'bottom_app_bar_theme.dart';
import 'bottom_sheet_theme.dart';
+import 'button_bar_theme.dart';
import 'button_theme.dart';
import 'card_theme.dart';
import 'chip_theme.dart';
@@ -180,6 +181,7 @@
PopupMenuThemeData popupMenuTheme,
MaterialBannerThemeData bannerTheme,
DividerThemeData dividerTheme,
+ ButtonBarThemeData buttonBarTheme,
}) {
brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark;
@@ -285,6 +287,7 @@
popupMenuTheme ??= const PopupMenuThemeData();
bannerTheme ??= const MaterialBannerThemeData();
dividerTheme ??= const DividerThemeData();
+ buttonBarTheme ??= const ButtonBarThemeData();
return ThemeData.raw(
brightness: brightness,
@@ -348,6 +351,7 @@
popupMenuTheme: popupMenuTheme,
bannerTheme: bannerTheme,
dividerTheme: dividerTheme,
+ buttonBarTheme: buttonBarTheme,
);
}
@@ -423,6 +427,7 @@
@required this.popupMenuTheme,
@required this.bannerTheme,
@required this.dividerTheme,
+ @required this.buttonBarTheme,
}) : assert(brightness != null),
assert(primaryColor != null),
assert(primaryColorBrightness != null),
@@ -480,7 +485,8 @@
assert(bottomSheetTheme != null),
assert(popupMenuTheme != 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
/// text styles of the optional [textTheme].
@@ -877,6 +883,9 @@
/// [VerticalDivider]s, etc.
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.
ThemeData copyWith({
Brightness brightness,
@@ -940,6 +949,7 @@
PopupMenuThemeData popupMenuTheme,
MaterialBannerThemeData bannerTheme,
DividerThemeData dividerTheme,
+ ButtonBarThemeData buttonBarTheme,
}) {
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
return ThemeData.raw(
@@ -1004,6 +1014,7 @@
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
bannerTheme: bannerTheme ?? this.bannerTheme,
dividerTheme: dividerTheme ?? this.dividerTheme,
+ buttonBarTheme: buttonBarTheme ?? this.buttonBarTheme,
);
}
@@ -1146,6 +1157,7 @@
popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t),
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t),
+ buttonBarTheme: ButtonBarThemeData.lerp(a.buttonBarTheme, b.buttonBarTheme, t),
);
}
@@ -1215,7 +1227,8 @@
(otherData.bottomSheetTheme == bottomSheetTheme) &&
(otherData.popupMenuTheme == popupMenuTheme) &&
(otherData.bannerTheme == bannerTheme) &&
- (otherData.dividerTheme == dividerTheme);
+ (otherData.dividerTheme == dividerTheme) &&
+ (otherData.buttonBarTheme == buttonBarTheme);
}
@override
@@ -1285,6 +1298,7 @@
popupMenuTheme,
bannerTheme,
dividerTheme,
+ buttonBarTheme,
];
return hashList(values);
}
@@ -1351,6 +1365,7 @@
properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme));
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme));
properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme));
+ properties.add(DiagnosticsProperty<ButtonBarThemeData>('buttonBarTheme', buttonBarTheme, defaultValue: defaultData.buttonBarTheme));
}
}
diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart
index 8eb322b..d5cf674 100644
--- a/packages/flutter/lib/src/material/time_picker.dart
+++ b/packages/flutter/lib/src/material/time_picker.dart
@@ -10,7 +10,6 @@
import 'package:flutter/widgets.dart';
import 'button_bar.dart';
-import 'button_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'dialog.dart';
@@ -1613,19 +1612,17 @@
),
);
- final Widget actions = ButtonTheme.bar(
- child: ButtonBar(
- children: <Widget>[
- FlatButton(
- child: Text(localizations.cancelButtonLabel),
- onPressed: _handleCancel,
- ),
- FlatButton(
- child: Text(localizations.okButtonLabel),
- onPressed: _handleOk,
- ),
- ],
- ),
+ final Widget actions = ButtonBar(
+ children: <Widget>[
+ FlatButton(
+ child: Text(localizations.cancelButtonLabel),
+ onPressed: _handleCancel,
+ ),
+ FlatButton(
+ child: Text(localizations.okButtonLabel),
+ onPressed: _handleOk,
+ ),
+ ],
);
final Dialog dialog = Dialog(
diff --git a/packages/flutter/test/material/button_bar_test.dart b/packages/flutter/test/material/button_bar_test.dart
index 2d18937..fa3c625 100644
--- a/packages/flutter/test/material/button_bar_test.dart
+++ b/packages/flutter/test/material/button_bar_test.dart
@@ -15,105 +15,282 @@
);
});
- testWidgets('ButtonBar has a min height of 52 when using ButtonBarLayoutBehavior.constrained', (WidgetTester tester) async {
- await tester.pumpWidget(
- SingleChildScrollView(
- child: ListBody(
- children: <Widget>[
- ButtonTheme.bar(
+ group('alignment', () {
+
+ testWidgets('default alignment is MainAxisAlignment.end', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ const MaterialApp(
+ home: ButtonBar(
+ children: <Widget>[
+ SizedBox(width: 10.0, height: 10.0),
+ ],
+ ),
+ )
+ );
+
+ final Finder child = find.byType(SizedBox);
+ // Should be positioned to the right of the bar,
+ expect(tester.getRect(child).left, 782.0); // bar width - default padding - 10
+ expect(tester.getRect(child).right, 792.0); // bar width - default padding
+ });
+
+ testWidgets('ButtonBarTheme.alignment overrides default', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ const MaterialApp(
+ home: ButtonBarTheme(
+ data: ButtonBarThemeData(
+ alignment: MainAxisAlignment.center,
+ ),
+ child: ButtonBar(
+ children: <Widget>[
+ SizedBox(width: 10.0, height: 10.0),
+ ],
+ ),
+ ),
+ )
+ );
+
+ final Finder child = find.byType(SizedBox);
+ // Should be positioned in the center
+ expect(tester.getRect(child).left, 395.0); // (bar width - padding) / 2 - 10 / 2
+ expect(tester.getRect(child).right, 405.0); // (bar width - padding) / 2 - 10 / 2 + 10
+ });
+
+ testWidgets('ButtonBar.alignment overrides ButtonBarTheme.alignment and default', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ const MaterialApp(
+ home: ButtonBarTheme(
+ data: ButtonBarThemeData(
+ alignment: MainAxisAlignment.center,
+ ),
+ child: ButtonBar(
+ alignment: MainAxisAlignment.start,
+ children: <Widget>[
+ SizedBox(width: 10.0, height: 10.0),
+ ],
+ ),
+ ),
+ )
+ );
+
+ final Finder child = find.byType(SizedBox);
+ // Should be positioned on the left
+ expect(tester.getRect(child).left, 8.0); // padding
+ expect(tester.getRect(child).right, 18.0); // padding + 10
+ });
+
+ });
+
+ group('mainAxisSize', () {
+
+ testWidgets('default mainAxisSize is MainAxisSize.max', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBar(
+ children: <Widget>[
+ Container(),
+ ],
+ ),
+ )
+ );
+
+ // ButtonBar uses a Row internally to implement this
+ final Row row = tester.widget(find.byType(Row));
+ expect(row.mainAxisSize, equals(MainAxisSize.max));
+ });
+
+ testWidgets('ButtonBarTheme.mainAxisSize overrides default', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBarTheme(
+ data: const ButtonBarThemeData(
+ mainAxisSize: MainAxisSize.min,
+ ),
+ child: ButtonBar(
+ children: <Widget>[
+ Container(),
+ ],
+ ),
+ ),
+ )
+ );
+
+ // ButtonBar uses a Row internally to implement this
+ final Row row = tester.widget(find.byType(Row));
+ expect(row.mainAxisSize, equals(MainAxisSize.min));
+ });
+
+ testWidgets('ButtonBar.mainAxisSize overrides ButtonBarTheme.mainAxisSize and default', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBarTheme(
+ data: const ButtonBarThemeData(
+ mainAxisSize: MainAxisSize.min,
+ ),
+ child: ButtonBar(
+ mainAxisSize: MainAxisSize.max,
+ children: <Widget>[
+ Container(),
+ ],
+ ),
+ ),
+ )
+ );
+
+ // ButtonBar uses a Row internally to implement this
+ final Row row = tester.widget(find.byType(Row));
+ expect(row.mainAxisSize, equals(MainAxisSize.max));
+ });
+
+ });
+
+ group('button properies override ButtonTheme', () {
+
+ testWidgets('default button properties override ButtonTheme properties', (WidgetTester tester) async {
+ BuildContext capturedContext;
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBar(
+ children: <Widget>[
+ Builder(builder: (BuildContext context) {
+ capturedContext = context;
+ return Container();
+ })
+ ],
+ ),
+ )
+ );
+ final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
+ expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
+ expect(buttonTheme.minWidth, equals(64.0));
+ expect(buttonTheme.height, equals(36.0));
+ expect(buttonTheme.padding, equals(const EdgeInsets.symmetric(horizontal: 8.0)));
+ expect(buttonTheme.alignedDropdown, equals(false));
+ expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.padded));
+ });
+
+ testWidgets('ButtonBarTheme button properties override defaults and ButtonTheme properties', (WidgetTester tester) async {
+ BuildContext capturedContext;
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBarTheme(
+ data: const ButtonBarThemeData(
+ buttonTextTheme: ButtonTextTheme.primary,
+ buttonMinWidth: 42.0,
+ buttonHeight: 84.0,
+ buttonPadding: EdgeInsets.fromLTRB(10, 20, 30, 40),
+ buttonAlignedDropdown: true,
layoutBehavior: ButtonBarLayoutBehavior.constrained,
- child: const Directionality(
- textDirection: TextDirection.ltr,
- child: ButtonBar(
- children: <Widget>[
- SizedBox(width: 10.0, height: 10.0),
- ],
- ),
- ),
),
- ],
- ),
- ),
- );
+ child: ButtonBar(
+ children: <Widget>[
+ Builder(builder: (BuildContext context) {
+ capturedContext = context;
+ return Container();
+ })
+ ],
+ ),
+ ),
+ )
+ );
+ final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
+ expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
+ expect(buttonTheme.minWidth, equals(42.0));
+ expect(buttonTheme.height, equals(84.0));
+ expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40)));
+ expect(buttonTheme.alignedDropdown, equals(true));
+ expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained));
+ });
- final Finder buttonBar = find.byType(ButtonBar);
- expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 52.0);
- });
-
- testWidgets('ButtonBar has padding applied when using ButtonBarLayoutBehavior.padded', (WidgetTester tester) async {
- await tester.pumpWidget(
- SingleChildScrollView(
- child: ListBody(
- children: <Widget>[
- ButtonTheme.bar(
+ testWidgets('ButtonBar button properties override ButtonBarTheme, defaults and ButtonTheme properties', (WidgetTester tester) async {
+ BuildContext capturedContext;
+ await tester.pumpWidget(
+ MaterialApp(
+ home: ButtonBarTheme(
+ data: const ButtonBarThemeData(
+ buttonTextTheme: ButtonTextTheme.accent,
+ buttonMinWidth: 4242.0,
+ buttonHeight: 8484.0,
+ buttonPadding: EdgeInsets.fromLTRB(50, 60, 70, 80),
+ buttonAlignedDropdown: false,
layoutBehavior: ButtonBarLayoutBehavior.padded,
- child: const Directionality(
+ ),
+ child: ButtonBar(
+ buttonTextTheme: ButtonTextTheme.primary,
+ buttonMinWidth: 42.0,
+ buttonHeight: 84.0,
+ buttonPadding: const EdgeInsets.fromLTRB(10, 20, 30, 40),
+ buttonAlignedDropdown: true,
+ layoutBehavior: ButtonBarLayoutBehavior.constrained,
+ children: <Widget>[
+ Builder(builder: (BuildContext context) {
+ capturedContext = context;
+ return Container();
+ })
+ ],
+ ),
+ ),
+ )
+ );
+ final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext);
+ expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary));
+ expect(buttonTheme.minWidth, equals(42.0));
+ expect(buttonTheme.height, equals(84.0));
+ expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40)));
+ expect(buttonTheme.alignedDropdown, equals(true));
+ expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained));
+ });
+
+ });
+
+ group('layoutBehavior', () {
+
+ testWidgets('ButtonBar has a min height of 52 when using ButtonBarLayoutBehavior.constrained', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ SingleChildScrollView(
+ child: ListBody(
+ children: const <Widget>[
+ Directionality(
textDirection: TextDirection.ltr,
child: ButtonBar(
+ layoutBehavior: ButtonBarLayoutBehavior.constrained,
children: <Widget>[
SizedBox(width: 10.0, height: 10.0),
],
),
),
- ),
- ],
+ ],
+ ),
),
- ),
- );
+ );
- final Finder buttonBar = find.byType(ButtonBar);
- expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0);
- });
+ final Finder buttonBar = find.byType(ButtonBar);
+ expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 52.0);
+ });
- testWidgets('ButtonBar FlatButton inherits Theme accentColor', (WidgetTester tester) async {
- // Regression test for https://github.com/flutter/flutter/issues/22789
-
- await tester.pumpWidget(
- MaterialApp(
- theme: ThemeData(accentColor: const Color(0x00000001)),
- home: Builder(
- builder: (BuildContext context) {
- return Center(
- child: ButtonTheme.bar(
+ testWidgets('ButtonBar has padding applied when using ButtonBarLayoutBehavior.padded', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ SingleChildScrollView(
+ child: ListBody(
+ children: const <Widget>[
+ Directionality(
+ textDirection: TextDirection.ltr,
child: ButtonBar(
+ layoutBehavior: ButtonBarLayoutBehavior.padded,
children: <Widget>[
- FlatButton(
- child: const Text('button'),
- onPressed: () {
- showDialog<void>(
- context: context,
- builder: (BuildContext context) {
- return AlertDialog( // puts its actions in a ButtonBar
- actions: <Widget>[
- FlatButton(
- onPressed: () { },
- child: const Text('enabled'),
- ),
- ],
- );
- },
- );
- },
- ),
+ SizedBox(width: 10.0, height: 10.0),
],
),
),
- );
- },
+ ],
+ ),
),
- ),
- );
+ );
- expect(tester.widget<RawMaterialButton>(find.byType(RawMaterialButton)).textStyle.color, const Color(0x00000001));
+ final Finder buttonBar = find.byType(ButtonBar);
+ expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0);
+ });
- // Show the dialog
- await tester.tap(find.text('button'));
- await tester.pumpAndSettle();
-
- final Finder dialogButton = find.ancestor(
- of: find.text('enabled'),
- matching: find.byType(RawMaterialButton),
- );
- expect(tester.widget<RawMaterialButton>(dialogButton).textStyle.color, const Color(0x00000001));
});
+
}
diff --git a/packages/flutter/test/material/button_bar_theme_test.dart b/packages/flutter/test/material/button_bar_theme_test.dart
new file mode 100644
index 0000000..d14da3a
--- /dev/null
+++ b/packages/flutter/test/material/button_bar_theme_test.dart
@@ -0,0 +1,150 @@
+// 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));
+ });
+}