mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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 35b6d668e1
)
Removed the use of ButtonTheme.bar in the Banner implementation.
* Updated documentation from PR review comments.
This commit is contained in:
parent
ae29174560
commit
9dce19e96f
@ -324,22 +324,20 @@ class TravelDestinationContent extends StatelessWidget {
|
||||
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'); },
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ export 'src/material/bottom_sheet.dart';
|
||||
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';
|
||||
|
@ -118,11 +118,9 @@ class MaterialBanner extends StatelessWidget {
|
||||
?? 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
|
||||
|
@ -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 @@ import 'raised_button.dart';
|
||||
|
||||
/// 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 @@ import 'raised_button.dart';
|
||||
/// * [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 @@ class ButtonBar extends StatelessWidget {
|
||||
|
||||
@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:
|
||||
|
240
packages/flutter/lib/src/material/button_bar_theme.dart
Normal file
240
packages/flutter/lib/src/material/button_bar_theme.dart
Normal file
@ -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;
|
||||
}
|
@ -120,20 +120,51 @@ class ButtonTheme extends InheritedWidget {
|
||||
}) : 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,
|
||||
|
@ -36,19 +36,17 @@ import 'theme.dart';
|
||||
/// 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 @@ import 'theme.dart';
|
||||
/// 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 {
|
||||
|
@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart';
|
||||
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 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {
|
||||
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(
|
||||
|
@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart';
|
||||
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 @@ class AlertDialog extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (actions != null) {
|
||||
children.add(ButtonTheme.bar(
|
||||
child: ButtonBar(
|
||||
children: actions,
|
||||
),
|
||||
children.add(ButtonBar(
|
||||
children: actions,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart';
|
||||
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 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -14,7 +14,6 @@ import 'package:flutter/gestures.dart' show DragStartBehavior;
|
||||
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 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: ButtonTheme.bar(
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: ButtonBar(
|
||||
children: widget.persistentFooterButtons,
|
||||
),
|
||||
),
|
||||
top: false,
|
||||
child: ButtonBar(
|
||||
children: widget.persistentFooterButtons,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -301,9 +301,10 @@ class SnackBar extends StatelessWidget {
|
||||
),
|
||||
];
|
||||
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 {
|
||||
|
@ -13,6 +13,7 @@ import 'app_bar_theme.dart';
|
||||
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 @@ class ThemeData extends Diagnosticable {
|
||||
PopupMenuThemeData popupMenuTheme,
|
||||
MaterialBannerThemeData bannerTheme,
|
||||
DividerThemeData dividerTheme,
|
||||
ButtonBarThemeData buttonBarTheme,
|
||||
}) {
|
||||
brightness ??= Brightness.light;
|
||||
final bool isDark = brightness == Brightness.dark;
|
||||
@ -285,6 +287,7 @@ class ThemeData extends Diagnosticable {
|
||||
popupMenuTheme ??= const PopupMenuThemeData();
|
||||
bannerTheme ??= const MaterialBannerThemeData();
|
||||
dividerTheme ??= const DividerThemeData();
|
||||
buttonBarTheme ??= const ButtonBarThemeData();
|
||||
|
||||
return ThemeData.raw(
|
||||
brightness: brightness,
|
||||
@ -348,6 +351,7 @@ class ThemeData extends Diagnosticable {
|
||||
popupMenuTheme: popupMenuTheme,
|
||||
bannerTheme: bannerTheme,
|
||||
dividerTheme: dividerTheme,
|
||||
buttonBarTheme: buttonBarTheme,
|
||||
);
|
||||
}
|
||||
|
||||
@ -423,6 +427,7 @@ class ThemeData extends Diagnosticable {
|
||||
@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 @@ class ThemeData extends Diagnosticable {
|
||||
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 @@ class ThemeData extends Diagnosticable {
|
||||
/// [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 @@ class ThemeData extends Diagnosticable {
|
||||
PopupMenuThemeData popupMenuTheme,
|
||||
MaterialBannerThemeData bannerTheme,
|
||||
DividerThemeData dividerTheme,
|
||||
ButtonBarThemeData buttonBarTheme,
|
||||
}) {
|
||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||
return ThemeData.raw(
|
||||
@ -1004,6 +1014,7 @@ class ThemeData extends Diagnosticable {
|
||||
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
|
||||
bannerTheme: bannerTheme ?? this.bannerTheme,
|
||||
dividerTheme: dividerTheme ?? this.dividerTheme,
|
||||
buttonBarTheme: buttonBarTheme ?? this.buttonBarTheme,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1146,6 +1157,7 @@ class ThemeData extends Diagnosticable {
|
||||
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 @@ class ThemeData extends Diagnosticable {
|
||||
(otherData.bottomSheetTheme == bottomSheetTheme) &&
|
||||
(otherData.popupMenuTheme == popupMenuTheme) &&
|
||||
(otherData.bannerTheme == bannerTheme) &&
|
||||
(otherData.dividerTheme == dividerTheme);
|
||||
(otherData.dividerTheme == dividerTheme) &&
|
||||
(otherData.buttonBarTheme == buttonBarTheme);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1285,6 +1298,7 @@ class ThemeData extends Diagnosticable {
|
||||
popupMenuTheme,
|
||||
bannerTheme,
|
||||
dividerTheme,
|
||||
buttonBarTheme,
|
||||
];
|
||||
return hashList(values);
|
||||
}
|
||||
@ -1351,6 +1365,7 @@ class ThemeData extends Diagnosticable {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
|
||||
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 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
|
||||
),
|
||||
);
|
||||
|
||||
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(
|
||||
|
@ -15,105 +15,282 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
}
|
||||
|
150
packages/flutter/test/material/button_bar_theme_test.dart
Normal file
150
packages/flutter/test/material/button_bar_theme_test.dart
Normal file
@ -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));
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user