Added support for M3 filled and filled tonal buttons. (#107382)
This commit is contained in:
parent
e6ed4c4635
commit
5454bab6c1
@ -103,6 +103,8 @@ Future<void> main(List<String> args) async {
|
||||
|
||||
AppBarTemplate('AppBar', '$materialLib/app_bar.dart', tokens).updateFile();
|
||||
ButtonTemplate('md.comp.elevated-button', 'ElevatedButton', '$materialLib/elevated_button.dart', tokens).updateFile();
|
||||
ButtonTemplate('md.comp.filled-button', 'FilledButton', '$materialLib/filled_button.dart', tokens).updateFile();
|
||||
ButtonTemplate('md.comp.filled-tonal-button', 'FilledTonalButton', '$materialLib/filled_button.dart', tokens).updateFile();
|
||||
ButtonTemplate('md.comp.outlined-button', 'OutlinedButton', '$materialLib/outlined_button.dart', tokens).updateFile();
|
||||
ButtonTemplate('md.comp.text-button', 'TextButton', '$materialLib/text_button.dart', tokens).updateFile();
|
||||
CardTemplate('Card', '$materialLib/card.dart', tokens).updateFile();
|
||||
|
@ -58,31 +58,9 @@ class ButtonTypesGroup extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
ElevatedButton(onPressed: onPressed, child: const Text('Elevated')),
|
||||
|
||||
// Use an ElevatedButton with specific style to implement the
|
||||
// 'Filled' type.
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
||||
onPressed: onPressed,
|
||||
child: const Text('Filled'),
|
||||
),
|
||||
|
||||
// Use an ElevatedButton with specific style to implement the
|
||||
// 'Filled Tonal' type.
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
|
||||
onPressed: onPressed,
|
||||
child: const Text('Filled Tonal'),
|
||||
),
|
||||
|
||||
FilledButton(onPressed: onPressed, child: const Text('Filled')),
|
||||
FilledButton.tonal(onPressed: onPressed, child: const Text('Filled Tonal')),
|
||||
OutlinedButton(onPressed: onPressed, child: const Text('Outlined')),
|
||||
|
||||
TextButton(onPressed: onPressed, child: const Text('Text')),
|
||||
],
|
||||
),
|
||||
|
61
examples/api/lib/material/filled_button/filled_button.0.dart
Normal file
61
examples/api/lib/material/filled_button/filled_button.0.dart
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flutter code sample for FilledButton
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const FilledButtonApp());
|
||||
}
|
||||
|
||||
class FilledButtonApp extends StatelessWidget {
|
||||
const FilledButtonApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('FilledButton Sample')),
|
||||
body: Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Column(children: <Widget>[
|
||||
const SizedBox(height: 30),
|
||||
const Text('Filled'),
|
||||
const SizedBox(height: 15),
|
||||
FilledButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Enabled'),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
const FilledButton(
|
||||
onPressed: null,
|
||||
child: Text('Disabled'),
|
||||
),
|
||||
]),
|
||||
const SizedBox(width: 30),
|
||||
Column(children: <Widget>[
|
||||
const SizedBox(height: 30),
|
||||
const Text('Filled tonal'),
|
||||
const SizedBox(height: 15),
|
||||
FilledButton.tonal(
|
||||
onPressed: () {},
|
||||
child: const Text('Enabled'),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
const FilledButton.tonal(
|
||||
onPressed: null,
|
||||
child: Text('Disabled'),
|
||||
),
|
||||
])
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -80,6 +80,8 @@ export 'src/material/expansion_panel.dart';
|
||||
export 'src/material/expansion_tile.dart';
|
||||
export 'src/material/expansion_tile_theme.dart';
|
||||
export 'src/material/feedback.dart';
|
||||
export 'src/material/filled_button.dart';
|
||||
export 'src/material/filled_button_theme.dart';
|
||||
export 'src/material/filter_chip.dart';
|
||||
export 'src/material/flexible_space_bar.dart';
|
||||
export 'src/material/floating_action_button.dart';
|
||||
|
@ -26,14 +26,16 @@ import 'theme_data.dart';
|
||||
/// from the themes or from app-specific sources.
|
||||
///
|
||||
/// This class is planned to be deprecated in a future release, see
|
||||
/// [ButtonStyleButton], the base class of [TextButton], [ElevatedButton], and
|
||||
/// [OutlinedButton].
|
||||
/// [ButtonStyleButton], the base class of [ElevatedButton], [FilledButton],
|
||||
/// [OutlinedButton] and [TextButton].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextButton], a simple flat button without a shadow.
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [OutlinedButton], a [TextButton] with a border outline.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
@Category(<String>['Material', 'Button'])
|
||||
class RawMaterialButton extends StatefulWidget {
|
||||
/// Create a button based on [Semantics], [Material], and [InkWell] widgets.
|
||||
|
@ -78,8 +78,8 @@ import 'theme_data.dart';
|
||||
/// useful to make relatively sweeping changes based on a few initial
|
||||
/// parameters with simple values. The button styleFrom() methods
|
||||
/// enable such sweeping changes. See for example:
|
||||
/// [TextButton.styleFrom], [ElevatedButton.styleFrom],
|
||||
/// [OutlinedButton.styleFrom].
|
||||
/// [ElevatedButton.styleFrom], [FilledButton.styleFrom],
|
||||
/// [OutlinedButton.styleFrom], [TextButton.styleFrom].
|
||||
///
|
||||
/// For example, to override the default text and icon colors for a
|
||||
/// [TextButton], as well as its overlay color, with all of the
|
||||
@ -119,8 +119,8 @@ import 'theme_data.dart';
|
||||
/// | Type | Flutter implementation |
|
||||
/// | :----------- | :---------------------- |
|
||||
/// | Elevated | [ElevatedButton] |
|
||||
/// | Filled | Styled [ElevatedButton] |
|
||||
/// | Filled Tonal | Styled [ElevatedButton] |
|
||||
/// | Filled | [FilledButton] |
|
||||
/// | Filled Tonal | [FilledButton.tonal] |
|
||||
/// | Outlined | [OutlinedButton] |
|
||||
/// | Text | [TextButton] |
|
||||
///
|
||||
@ -132,9 +132,10 @@ import 'theme_data.dart';
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextButtonTheme], the theme for [TextButton]s.
|
||||
/// * [ElevatedButtonTheme], the theme for [ElevatedButton]s.
|
||||
/// * [FilledButtonTheme], the theme for [FilledButton]s.
|
||||
/// * [OutlinedButtonTheme], the theme for [OutlinedButton]s.
|
||||
/// * [TextButtonTheme], the theme for [TextButton]s.
|
||||
@immutable
|
||||
class ButtonStyle with Diagnosticable {
|
||||
/// Create a [ButtonStyle].
|
||||
|
@ -21,10 +21,13 @@ import 'theme_data.dart';
|
||||
/// Concrete subclasses must override [defaultStyleOf] and [themeStyleOf].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextButton], a simple ButtonStyleButton without a shadow.
|
||||
/// * [ElevatedButton], a filled ButtonStyleButton whose material elevates when pressed.
|
||||
/// * [OutlinedButton], similar to [TextButton], but with an outline.
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
/// * <https://m3.material.io/components/buttons/overview>, an overview of each of
|
||||
/// the Material Design button types and how they should be used in designs.
|
||||
abstract class ButtonStyleButton extends StatefulWidget {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
@ -191,9 +194,10 @@ abstract class ButtonStyleButton extends StatefulWidget {
|
||||
/// See also:
|
||||
///
|
||||
/// * [ButtonStyleButton], the [StatefulWidget] subclass for which this class is the [State].
|
||||
/// * [TextButton], a simple button without a shadow.
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled ButtonStyleButton that doesn't elevate when pressed.
|
||||
/// * [OutlinedButton], similar to [TextButton], but with an outline.
|
||||
/// * [TextButton], a simple button without a shadow.
|
||||
class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStateMixin {
|
||||
AnimationController? controller;
|
||||
double? elevation;
|
||||
|
@ -48,9 +48,10 @@ enum ButtonBarLayoutBehavior {
|
||||
/// This class is planned to be deprecated in a future release.
|
||||
/// Please use one or more of these buttons and associated themes instead:
|
||||
///
|
||||
/// * [TextButton], [TextButtonTheme], [TextButtonThemeData],
|
||||
/// * [ElevatedButton], [ElevatedButtonTheme], [ElevatedButtonThemeData],
|
||||
/// * [FilledButton], [FilledButtonTheme], [FilledButtonThemeData],
|
||||
/// * [OutlinedButton], [OutlinedButtonTheme], [OutlinedButtonThemeData]
|
||||
/// * [TextButton], [TextButtonTheme], [TextButtonThemeData],
|
||||
///
|
||||
/// A button theme can be specified as part of the overall Material theme
|
||||
/// using [ThemeData.buttonTheme]. The Material theme's button theme data
|
||||
|
@ -54,8 +54,10 @@ import 'theme_data.dart';
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextButton], a simple flat button without a shadow.
|
||||
/// * [OutlinedButton], a [TextButton] with a border outline.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
/// * <https://material.io/design/components/buttons.html>
|
||||
/// * <https://m3.material.io/components/buttons>
|
||||
class ElevatedButton extends ButtonStyleButton {
|
||||
|
737
packages/flutter/lib/src/material/filled_button.dart
Normal file
737
packages/flutter/lib/src/material/filled_button.dart
Normal file
@ -0,0 +1,737 @@
|
||||
// Copyright 2014 The Flutter 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:math' as math;
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'button_style.dart';
|
||||
import 'button_style_button.dart';
|
||||
import 'color_scheme.dart';
|
||||
import 'constants.dart';
|
||||
import 'filled_button_theme.dart';
|
||||
import 'ink_well.dart';
|
||||
import 'material_state.dart';
|
||||
import 'theme.dart';
|
||||
import 'theme_data.dart';
|
||||
|
||||
enum _FilledButtonVariant { filled, tonal }
|
||||
|
||||
/// A Material Design filled button.
|
||||
///
|
||||
/// Filled buttons have the most visual impact after the [FloatingActionButton],
|
||||
/// and should be used for important, final actions that complete a flow,
|
||||
/// like **Save**, **Join now**, or **Confirm**.
|
||||
///
|
||||
/// A filled button is a label [child] displayed on a [Material]
|
||||
/// widget. The label's [Text] and [Icon] widgets are displayed in
|
||||
/// [style]'s [ButtonStyle.foregroundColor] and the button's filled
|
||||
/// background is the [ButtonStyle.backgroundColor].
|
||||
///
|
||||
/// The filled button's default style is defined by
|
||||
/// [defaultStyleOf]. The style of this filled button can be
|
||||
/// overridden with its [style] parameter. The style of all filled
|
||||
/// buttons in a subtree can be overridden with the
|
||||
/// [FilledButtonTheme], and the style of all of the filled
|
||||
/// buttons in an app can be overridden with the [Theme]'s
|
||||
/// [ThemeData.filledButtonTheme] property.
|
||||
///
|
||||
/// The static [styleFrom] method is a convenient way to create a
|
||||
/// filled button [ButtonStyle] from simple values.
|
||||
///
|
||||
/// If [onPressed] and [onLongPress] callbacks are null, then the
|
||||
/// button will be disabled.
|
||||
///
|
||||
/// To create a 'filled tonal' button, use [FilledButton.tonal].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample produces enabled and disabled filled and filled tonal
|
||||
/// buttons.
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/filled_button/filled_button.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
/// * <https://material.io/design/components/buttons.html>
|
||||
/// * <https://m3.material.io/components/buttons>
|
||||
class FilledButton extends ButtonStyleButton {
|
||||
/// Create a FilledButton.
|
||||
///
|
||||
/// The [autofocus] and [clipBehavior] arguments must not be null.
|
||||
const FilledButton({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
super.autofocus = false,
|
||||
super.clipBehavior = Clip.none,
|
||||
super.statesController,
|
||||
required super.child,
|
||||
}) : _variant = _FilledButtonVariant.filled;
|
||||
|
||||
/// Create a filled button from [icon] and [label].
|
||||
///
|
||||
/// The icon and label are arranged in a row with padding at the start and end
|
||||
/// and a gap between them.
|
||||
///
|
||||
/// The [icon] and [label] arguments must not be null.
|
||||
factory FilledButton.icon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
VoidCallback? onLongPress,
|
||||
ValueChanged<bool>? onHover,
|
||||
ValueChanged<bool>? onFocusChange,
|
||||
ButtonStyle? style,
|
||||
FocusNode? focusNode,
|
||||
bool? autofocus,
|
||||
Clip? clipBehavior,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
}) = _FilledButtonWithIcon;
|
||||
|
||||
/// Create a tonal variant of FilledButton.
|
||||
///
|
||||
/// A filled tonal button is an alternative middle ground between
|
||||
/// [FilledButton] and [OutlinedButton]. They’re useful in contexts where
|
||||
/// a lower-priority button requires slightly more emphasis than an
|
||||
/// outline would give, such as "Next" in an onboarding flow.
|
||||
///
|
||||
/// The [autofocus] and [clipBehavior] arguments must not be null.
|
||||
const FilledButton.tonal({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
super.autofocus = false,
|
||||
super.clipBehavior = Clip.none,
|
||||
super.statesController,
|
||||
required super.child,
|
||||
}) : _variant = _FilledButtonVariant.tonal;
|
||||
|
||||
/// Create a filled tonal button from [icon] and [label].
|
||||
///
|
||||
/// The icon and label are arranged in a row with padding at the start and end
|
||||
/// and a gap between them.
|
||||
///
|
||||
/// The [icon] and [label] arguments must not be null.
|
||||
factory FilledButton.tonalIcon({
|
||||
Key? key,
|
||||
required VoidCallback? onPressed,
|
||||
VoidCallback? onLongPress,
|
||||
ValueChanged<bool>? onHover,
|
||||
ValueChanged<bool>? onFocusChange,
|
||||
ButtonStyle? style,
|
||||
FocusNode? focusNode,
|
||||
bool? autofocus,
|
||||
Clip? clipBehavior,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
}) {
|
||||
return _FilledButtonWithIcon.tonal(
|
||||
key: key,
|
||||
onPressed: onPressed,
|
||||
onLongPress: onLongPress,
|
||||
onHover: onHover,
|
||||
onFocusChange: onFocusChange,
|
||||
style: style,
|
||||
focusNode: focusNode,
|
||||
autofocus: autofocus,
|
||||
clipBehavior: clipBehavior,
|
||||
icon: icon,
|
||||
label: label,
|
||||
);
|
||||
}
|
||||
|
||||
/// A static convenience method that constructs a filled button
|
||||
/// [ButtonStyle] given simple values.
|
||||
///
|
||||
/// The [foregroundColor], and [disabledForegroundColor] colors are used to create a
|
||||
/// [MaterialStateProperty] [ButtonStyle.foregroundColor] value. The
|
||||
/// [backgroundColor] and [disabledBackgroundColor] are used to create a
|
||||
/// [MaterialStateProperty] [ButtonStyle.backgroundColor] value.
|
||||
///
|
||||
/// The button's elevations are defined relative to the [elevation]
|
||||
/// parameter. The disabled elevation is the same as the parameter
|
||||
/// value, [elevation] + 2 is used when the button is hovered
|
||||
/// or focused, and elevation + 6 is used when the button is pressed.
|
||||
///
|
||||
/// Similarly, the [enabledMouseCursor] and [disabledMouseCursor]
|
||||
/// parameters are used to construct [ButtonStyle.mouseCursor].
|
||||
///
|
||||
/// All of the other parameters are either used directly or used to
|
||||
/// create a [MaterialStateProperty] with a single value for all
|
||||
/// states.
|
||||
///
|
||||
/// All parameters default to null, by default this method returns
|
||||
/// a [ButtonStyle] that doesn't override anything.
|
||||
///
|
||||
/// For example, to override the default text and icon colors for a
|
||||
/// [FilledButton], as well as its overlay color, with all of the
|
||||
/// standard opacity adjustments for the pressed, focused, and
|
||||
/// hovered states, one could write:
|
||||
///
|
||||
/// ```dart
|
||||
/// FilledButton(
|
||||
/// style: FilledButton.styleFrom(foregroundColor: Colors.green),
|
||||
/// onPressed: () {},
|
||||
/// child: const Text('Filled button'),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// or for a Filled tonal variant:
|
||||
/// ```dart
|
||||
/// FilledButton.tonal(
|
||||
/// style: FilledButton.styleFrom(foregroundColor: Colors.green),
|
||||
/// onPressed: () {},
|
||||
/// child: const Text('Filled tonal button'),
|
||||
/// );
|
||||
/// ```
|
||||
static ButtonStyle styleFrom({
|
||||
Color? foregroundColor,
|
||||
Color? backgroundColor,
|
||||
Color? disabledForegroundColor,
|
||||
Color? disabledBackgroundColor,
|
||||
Color? shadowColor,
|
||||
Color? surfaceTintColor,
|
||||
double? elevation,
|
||||
TextStyle? textStyle,
|
||||
EdgeInsetsGeometry? padding,
|
||||
Size? minimumSize,
|
||||
Size? fixedSize,
|
||||
Size? maximumSize,
|
||||
BorderSide? side,
|
||||
OutlinedBorder? shape,
|
||||
MouseCursor? enabledMouseCursor,
|
||||
MouseCursor? disabledMouseCursor,
|
||||
VisualDensity? visualDensity,
|
||||
MaterialTapTargetSize? tapTargetSize,
|
||||
Duration? animationDuration,
|
||||
bool? enableFeedback,
|
||||
AlignmentGeometry? alignment,
|
||||
InteractiveInkFeatureFactory? splashFactory,
|
||||
}) {
|
||||
final MaterialStateProperty<Color?>? backgroundColorProp =
|
||||
(backgroundColor == null && disabledBackgroundColor == null)
|
||||
? null
|
||||
: _FilledButtonDefaultColor(backgroundColor, disabledBackgroundColor);
|
||||
final Color? foreground = foregroundColor;
|
||||
final Color? disabledForeground = disabledForegroundColor;
|
||||
final MaterialStateProperty<Color?>? foregroundColorProp =
|
||||
(foreground == null && disabledForeground == null)
|
||||
? null
|
||||
: _FilledButtonDefaultColor(foreground, disabledForeground);
|
||||
final MaterialStateProperty<Color?>? overlayColor = (foreground == null)
|
||||
? null
|
||||
: _FilledButtonDefaultOverlay(foreground);
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor =
|
||||
(enabledMouseCursor == null && disabledMouseCursor == null)
|
||||
? null
|
||||
: _FilledButtonDefaultMouseCursor(enabledMouseCursor, disabledMouseCursor);
|
||||
|
||||
return ButtonStyle(
|
||||
textStyle: MaterialStatePropertyAll<TextStyle?>(textStyle),
|
||||
backgroundColor: backgroundColorProp,
|
||||
foregroundColor: foregroundColorProp,
|
||||
overlayColor: overlayColor,
|
||||
shadowColor: ButtonStyleButton.allOrNull<Color>(shadowColor),
|
||||
surfaceTintColor: ButtonStyleButton.allOrNull<Color>(surfaceTintColor),
|
||||
elevation: ButtonStyleButton.allOrNull(elevation),
|
||||
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
|
||||
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
|
||||
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
|
||||
maximumSize: ButtonStyleButton.allOrNull<Size>(maximumSize),
|
||||
side: ButtonStyleButton.allOrNull<BorderSide>(side),
|
||||
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
|
||||
mouseCursor: mouseCursor,
|
||||
visualDensity: visualDensity,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
enableFeedback: enableFeedback,
|
||||
alignment: alignment,
|
||||
splashFactory: splashFactory,
|
||||
);
|
||||
}
|
||||
|
||||
final _FilledButtonVariant _variant;
|
||||
|
||||
/// Defines the button's default appearance.
|
||||
///
|
||||
/// The button [child]'s [Text] and [Icon] widgets are rendered with
|
||||
/// the [ButtonStyle]'s foreground color. The button's [InkWell] adds
|
||||
/// the style's overlay color when the button is focused, hovered
|
||||
/// or pressed. The button's background color becomes its [Material]
|
||||
/// color.
|
||||
///
|
||||
/// All of the ButtonStyle's defaults appear below. In this list
|
||||
/// "Theme.foo" is shorthand for `Theme.of(context).foo`. Color
|
||||
/// scheme values like "onSurface(0.38)" are shorthand for
|
||||
/// `onSurface.withOpacity(0.38)`. [MaterialStateProperty] valued
|
||||
/// properties that are not followed by a sublist have the same
|
||||
/// value for all states, otherwise the values are as specified for
|
||||
/// each state, and "others" means all other states.
|
||||
///
|
||||
/// The `textScaleFactor` is the value of
|
||||
/// `MediaQuery.of(context).textScaleFactor` and the names of the
|
||||
/// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been
|
||||
/// abbreviated for readability.
|
||||
///
|
||||
/// The color of the [ButtonStyle.textStyle] is not used, the
|
||||
/// [ButtonStyle.foregroundColor] color is used instead.
|
||||
///
|
||||
/// * `textStyle` - Theme.textTheme.labelLarge
|
||||
/// * `backgroundColor`
|
||||
/// * disabled - Theme.colorScheme.onSurface(0.12)
|
||||
/// * others - Theme.colorScheme.secondaryContainer
|
||||
/// * `foregroundColor`
|
||||
/// * disabled - Theme.colorScheme.onSurface(0.38)
|
||||
/// * others - Theme.colorScheme.onSecondaryContainer
|
||||
/// * `overlayColor`
|
||||
/// * hovered - Theme.colorScheme.onSecondaryContainer(0.08)
|
||||
/// * focused or pressed - Theme.colorScheme.onSecondaryContainer(0.12)
|
||||
/// * `shadowColor` - Theme.colorScheme.shadow
|
||||
/// * `surfaceTintColor` - null
|
||||
/// * `elevation`
|
||||
/// * disabled - 0
|
||||
/// * default - 0
|
||||
/// * hovered - 1
|
||||
/// * focused or pressed - 0
|
||||
/// * `padding`
|
||||
/// * `textScaleFactor <= 1` - horizontal(16)
|
||||
/// * `1 < textScaleFactor <= 2` - lerp(horizontal(16), horizontal(8))
|
||||
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
|
||||
/// * `3 < textScaleFactor` - horizontal(4)
|
||||
/// * `minimumSize` - Size(64, 40)
|
||||
/// * `fixedSize` - null
|
||||
/// * `maximumSize` - Size.infinite
|
||||
/// * `side` - null
|
||||
/// * `shape` - StadiumBorder()
|
||||
/// * `mouseCursor`
|
||||
/// * disabled - SystemMouseCursors.basic
|
||||
/// * others - SystemMouseCursors.click
|
||||
/// * `visualDensity` - Theme.visualDensity
|
||||
/// * `tapTargetSize` - Theme.materialTapTargetSize
|
||||
/// * `animationDuration` - kThemeChangeDuration
|
||||
/// * `enableFeedback` - true
|
||||
/// * `alignment` - Alignment.center
|
||||
/// * `splashFactory` - Theme.splashFactory
|
||||
///
|
||||
/// The default padding values for the [FilledButton.icon] factory are slightly different:
|
||||
///
|
||||
/// * `padding`
|
||||
/// * `textScaleFactor <= 1` - start(12) end(16)
|
||||
/// * `1 < textScaleFactor <= 2` - lerp(start(12) end(16), horizontal(8))
|
||||
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
|
||||
/// * `3 < textScaleFactor` - horizontal(4)
|
||||
///
|
||||
/// The default value for `side`, which defines the appearance of the button's
|
||||
/// outline, is null. That means that the outline is defined by the button
|
||||
/// shape's [OutlinedBorder.side]. Typically the default value of an
|
||||
/// [OutlinedBorder]'s side is [BorderSide.none], so an outline is not drawn.
|
||||
///
|
||||
@override
|
||||
ButtonStyle defaultStyleOf(BuildContext context) {
|
||||
switch (_variant) {
|
||||
case _FilledButtonVariant.filled:
|
||||
return _FilledButtonDefaultsM3(context);
|
||||
case _FilledButtonVariant.tonal:
|
||||
return _FilledTonalButtonDefaultsM3(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [FilledButtonThemeData.style] of the closest
|
||||
/// [FilledButtonTheme] ancestor.
|
||||
@override
|
||||
ButtonStyle? themeStyleOf(BuildContext context) {
|
||||
return FilledButtonTheme.of(context).style;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeInsetsGeometry _scaledPadding(BuildContext context) {
|
||||
return ButtonStyleButton.scaledPadding(
|
||||
const EdgeInsets.symmetric(horizontal: 16),
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultColor extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_FilledButtonDefaultColor(this.color, this.disabled);
|
||||
|
||||
final Color? color;
|
||||
final Color? disabled;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabled;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultOverlay extends MaterialStateProperty<Color?> with Diagnosticable {
|
||||
_FilledButtonDefaultOverlay(this.overlay);
|
||||
|
||||
final Color overlay;
|
||||
|
||||
@override
|
||||
Color? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return overlay.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed)) {
|
||||
return overlay.withOpacity(0.12);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _FilledButtonDefaultMouseCursor extends MaterialStateProperty<MouseCursor?> with Diagnosticable {
|
||||
_FilledButtonDefaultMouseCursor(this.enabledCursor, this.disabledCursor);
|
||||
|
||||
final MouseCursor? enabledCursor;
|
||||
final MouseCursor? disabledCursor;
|
||||
|
||||
@override
|
||||
MouseCursor? resolve(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return disabledCursor;
|
||||
}
|
||||
return enabledCursor;
|
||||
}
|
||||
}
|
||||
|
||||
class _FilledButtonWithIcon extends FilledButton {
|
||||
_FilledButtonWithIcon({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
bool? autofocus,
|
||||
Clip? clipBehavior,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
}) : assert(icon != null),
|
||||
assert(label != null),
|
||||
super(
|
||||
autofocus: autofocus ?? false,
|
||||
clipBehavior: clipBehavior ?? Clip.none,
|
||||
child: _FilledButtonWithIconChild(icon: icon, label: label)
|
||||
);
|
||||
|
||||
_FilledButtonWithIcon.tonal({
|
||||
super.key,
|
||||
required super.onPressed,
|
||||
super.onLongPress,
|
||||
super.onHover,
|
||||
super.onFocusChange,
|
||||
super.style,
|
||||
super.focusNode,
|
||||
bool? autofocus,
|
||||
Clip? clipBehavior,
|
||||
required Widget icon,
|
||||
required Widget label,
|
||||
}) : assert(icon != null),
|
||||
assert(label != null),
|
||||
super.tonal(
|
||||
autofocus: autofocus ?? false,
|
||||
clipBehavior: clipBehavior ?? Clip.none,
|
||||
child: _FilledButtonWithIconChild(icon: icon, label: label)
|
||||
);
|
||||
|
||||
@override
|
||||
ButtonStyle defaultStyleOf(BuildContext context) {
|
||||
final EdgeInsetsGeometry scaledPadding = ButtonStyleButton.scaledPadding(
|
||||
const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0),
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0),
|
||||
MediaQuery.maybeOf(context)?.textScaleFactor ?? 1,
|
||||
);
|
||||
return super.defaultStyleOf(context).copyWith(
|
||||
padding: MaterialStatePropertyAll<EdgeInsetsGeometry>(scaledPadding),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FilledButtonWithIconChild extends StatelessWidget {
|
||||
const _FilledButtonWithIconChild({ required this.label, required this.icon });
|
||||
|
||||
final Widget label;
|
||||
final Widget icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1;
|
||||
// Adjust the gap based on the text scale factor. Start at 8, and lerp
|
||||
// to 4 based on how large the text is.
|
||||
final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[icon, SizedBox(width: gap), Flexible(child: label)],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - FilledButton
|
||||
|
||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||
// "END GENERATED" comments are generated from data in the Material
|
||||
// Design token database by the script:
|
||||
// dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
// Token database version: v0_101
|
||||
|
||||
class _FilledButtonDefaultsM3 extends ButtonStyle {
|
||||
_FilledButtonDefaultsM3(this.context)
|
||||
: super(
|
||||
animationDuration: kThemeChangeDuration,
|
||||
enableFeedback: true,
|
||||
alignment: Alignment.center,
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
MaterialStateProperty<TextStyle?> get textStyle =>
|
||||
MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get backgroundColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _colors.onSurface.withOpacity(0.12);
|
||||
}
|
||||
return _colors.primary;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get foregroundColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _colors.onSurface.withOpacity(0.38);
|
||||
}
|
||||
return _colors.onPrimary;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get overlayColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return _colors.onPrimary.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return _colors.onPrimary.withOpacity(0.12);
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return _colors.onPrimary.withOpacity(0.12);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color>? get shadowColor =>
|
||||
ButtonStyleButton.allOrNull<Color>(_colors.shadow);
|
||||
|
||||
// No default surface tint color
|
||||
|
||||
@override
|
||||
MaterialStateProperty<double>? get elevation =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return 1.0;
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return 0.0;
|
||||
}
|
||||
return 0.0;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
|
||||
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Size>? get minimumSize =>
|
||||
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
|
||||
|
||||
// No default fixedSize
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Size>? get maximumSize =>
|
||||
ButtonStyleButton.allOrNull<Size>(Size.infinite);
|
||||
|
||||
// No default side
|
||||
|
||||
@override
|
||||
MaterialStateProperty<OutlinedBorder>? get shape =>
|
||||
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
|
||||
|
||||
@override
|
||||
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.basic;
|
||||
}
|
||||
return SystemMouseCursors.click;
|
||||
});
|
||||
|
||||
@override
|
||||
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
|
||||
|
||||
@override
|
||||
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
|
||||
|
||||
@override
|
||||
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
|
||||
}
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES - FilledButton
|
||||
|
||||
// BEGIN GENERATED TOKEN PROPERTIES - FilledTonalButton
|
||||
|
||||
// Do not edit by hand. The code between the "BEGIN GENERATED" and
|
||||
// "END GENERATED" comments are generated from data in the Material
|
||||
// Design token database by the script:
|
||||
// dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||
|
||||
// Token database version: v0_101
|
||||
|
||||
class _FilledTonalButtonDefaultsM3 extends ButtonStyle {
|
||||
_FilledTonalButtonDefaultsM3(this.context)
|
||||
: super(
|
||||
animationDuration: kThemeChangeDuration,
|
||||
enableFeedback: true,
|
||||
alignment: Alignment.center,
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
MaterialStateProperty<TextStyle?> get textStyle =>
|
||||
MaterialStatePropertyAll<TextStyle?>(Theme.of(context).textTheme.labelLarge);
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get backgroundColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _colors.onSurface.withOpacity(0.12);
|
||||
}
|
||||
return _colors.secondaryContainer;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get foregroundColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _colors.onSurface.withOpacity(0.38);
|
||||
}
|
||||
return _colors.onSecondaryContainer;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color?>? get overlayColor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return _colors.onSecondaryContainer.withOpacity(0.08);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return _colors.onSecondaryContainer.withOpacity(0.12);
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return _colors.onSecondaryContainer.withOpacity(0.12);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Color>? get shadowColor =>
|
||||
ButtonStyleButton.allOrNull<Color>(_colors.shadow);
|
||||
|
||||
// No default surface tint color
|
||||
|
||||
@override
|
||||
MaterialStateProperty<double>? get elevation =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return 1.0;
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (states.contains(MaterialState.pressed)) {
|
||||
return 0.0;
|
||||
}
|
||||
return 0.0;
|
||||
});
|
||||
|
||||
@override
|
||||
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
|
||||
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(_scaledPadding(context));
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Size>? get minimumSize =>
|
||||
ButtonStyleButton.allOrNull<Size>(const Size(64.0, 40.0));
|
||||
|
||||
// No default fixedSize
|
||||
|
||||
@override
|
||||
MaterialStateProperty<Size>? get maximumSize =>
|
||||
ButtonStyleButton.allOrNull<Size>(Size.infinite);
|
||||
|
||||
// No default side
|
||||
|
||||
@override
|
||||
MaterialStateProperty<OutlinedBorder>? get shape =>
|
||||
ButtonStyleButton.allOrNull<OutlinedBorder>(const StadiumBorder());
|
||||
|
||||
@override
|
||||
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
|
||||
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return SystemMouseCursors.basic;
|
||||
}
|
||||
return SystemMouseCursors.click;
|
||||
});
|
||||
|
||||
@override
|
||||
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
|
||||
|
||||
@override
|
||||
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
|
||||
|
||||
@override
|
||||
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
|
||||
}
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES - FilledTonalButton
|
128
packages/flutter/lib/src/material/filled_button_theme.dart
Normal file
128
packages/flutter/lib/src/material/filled_button_theme.dart
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2014 The Flutter 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/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'button_style.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Examples can assume:
|
||||
// late BuildContext context;
|
||||
|
||||
/// A [ButtonStyle] that overrides the default appearance of
|
||||
/// [FilledButton]s when it's used with [FilledButtonTheme] or with the
|
||||
/// overall [Theme]'s [ThemeData.filledButtonTheme].
|
||||
///
|
||||
/// The [style]'s properties override [FilledButton]'s default style,
|
||||
/// i.e. the [ButtonStyle] returned by [FilledButton.defaultStyleOf]. Only
|
||||
/// the style's non-null property values or resolved non-null
|
||||
/// [MaterialStateProperty] values are used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [FilledButtonTheme], the theme which is configured with this class.
|
||||
/// * [FilledButton.defaultStyleOf], which returns the default [ButtonStyle]
|
||||
/// for text buttons.
|
||||
/// * [FilledButton.styleFrom], which converts simple values into a
|
||||
/// [ButtonStyle] that's consistent with [FilledButton]'s defaults.
|
||||
/// * [MaterialStateProperty.resolve], "resolve" a material state property
|
||||
/// to a simple value based on a set of [MaterialState]s.
|
||||
/// * [ThemeData.filledButtonTheme], which can be used to override the default
|
||||
/// [ButtonStyle] for [FilledButton]s below the overall [Theme].
|
||||
@immutable
|
||||
class FilledButtonThemeData with Diagnosticable {
|
||||
/// Creates an [FilledButtonThemeData].
|
||||
///
|
||||
/// The [style] may be null.
|
||||
const FilledButtonThemeData({ this.style });
|
||||
|
||||
/// Overrides for [FilledButton]'s default style.
|
||||
///
|
||||
/// Non-null properties or non-null resolved [MaterialStateProperty]
|
||||
/// values override the [ButtonStyle] returned by
|
||||
/// [FilledButton.defaultStyleOf].
|
||||
///
|
||||
/// If [style] is null, then this theme doesn't override anything.
|
||||
final ButtonStyle? style;
|
||||
|
||||
/// Linearly interpolate between two filled button themes.
|
||||
static FilledButtonThemeData? lerp(FilledButtonThemeData? a, FilledButtonThemeData? b, double t) {
|
||||
assert (t != null);
|
||||
if (a == null && b == null) {
|
||||
return null;
|
||||
}
|
||||
return FilledButtonThemeData(
|
||||
style: ButtonStyle.lerp(a?.style, b?.style, t),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => style.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
return other is FilledButtonThemeData && other.style == style;
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<ButtonStyle>('style', style, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the default [ButtonStyle] of its [FilledButton] descendants.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [FilledButtonThemeData], which is used to configure this theme.
|
||||
/// * [FilledButton.defaultStyleOf], which returns the default [ButtonStyle]
|
||||
/// for filled buttons.
|
||||
/// * [FilledButton.styleFrom], which converts simple values into a
|
||||
/// [ButtonStyle] that's consistent with [FilledButton]'s defaults.
|
||||
/// * [ThemeData.filledButtonTheme], which can be used to override the default
|
||||
/// [ButtonStyle] for [FilledButton]s below the overall [Theme].
|
||||
class FilledButtonTheme extends InheritedTheme {
|
||||
/// Create a [FilledButtonTheme].
|
||||
///
|
||||
/// The [data] parameter must not be null.
|
||||
const FilledButtonTheme({
|
||||
super.key,
|
||||
required this.data,
|
||||
required super.child,
|
||||
}) : assert(data != null);
|
||||
|
||||
/// The configuration of this theme.
|
||||
final FilledButtonThemeData data;
|
||||
|
||||
/// The closest instance of this class that encloses the given context.
|
||||
///
|
||||
/// If there is no enclosing [FilledButtonTheme] widget, then
|
||||
/// [ThemeData.filledButtonTheme] is used.
|
||||
///
|
||||
/// Typical usage is as follows:
|
||||
///
|
||||
/// ```dart
|
||||
/// FilledButtonThemeData theme = FilledButtonTheme.of(context);
|
||||
/// ```
|
||||
static FilledButtonThemeData of(BuildContext context) {
|
||||
final FilledButtonTheme? buttonTheme = context.dependOnInheritedWidgetOfExactType<FilledButtonTheme>();
|
||||
return buttonTheme?.data ?? Theme.of(context).filledButtonTheme;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget wrap(BuildContext context, Widget child) {
|
||||
return FilledButtonTheme(data: data, child: child);
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(FilledButtonTheme oldWidget) => data != oldWidget.data;
|
||||
}
|
@ -59,8 +59,10 @@ import 'theme_data.dart';
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ElevatedButton], a filled Material Design button with a shadow.
|
||||
/// * [TextButton], a Material Design button without a shadow.
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [TextButton], a button with no outline or fill color.
|
||||
/// * <https://material.io/design/components/buttons.html>
|
||||
/// * <https://m3.material.io/components/buttons>
|
||||
class OutlinedButton extends ButtonStyleButton {
|
||||
|
@ -66,8 +66,10 @@ import 'theme_data.dart';
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [OutlinedButton], a [TextButton] with a border outline.
|
||||
/// * [ElevatedButton], a filled button whose material elevates when pressed.
|
||||
/// * [FilledButton], a filled button that doesn't elevate when pressed.
|
||||
/// * [FilledButton.tonal], a filled button variant that uses a secondary fill color.
|
||||
/// * [OutlinedButton], a button with an outlined border and no fill color.
|
||||
/// * <https://material.io/design/components/buttons.html>
|
||||
/// * <https://m3.material.io/components/buttons>
|
||||
class TextButton extends ButtonStyleButton {
|
||||
|
@ -26,6 +26,7 @@ import 'divider_theme.dart';
|
||||
import 'drawer_theme.dart';
|
||||
import 'elevated_button_theme.dart';
|
||||
import 'expansion_tile_theme.dart';
|
||||
import 'filled_button_theme.dart';
|
||||
import 'floating_action_button_theme.dart';
|
||||
import 'icon_button_theme.dart';
|
||||
import 'ink_ripple.dart';
|
||||
@ -343,6 +344,7 @@ class ThemeData with Diagnosticable {
|
||||
DrawerThemeData? drawerTheme,
|
||||
ElevatedButtonThemeData? elevatedButtonTheme,
|
||||
ExpansionTileThemeData? expansionTileTheme,
|
||||
FilledButtonThemeData? filledButtonTheme,
|
||||
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
||||
IconButtonThemeData? iconButtonTheme,
|
||||
ListTileThemeData? listTileTheme,
|
||||
@ -564,6 +566,7 @@ class ThemeData with Diagnosticable {
|
||||
dividerTheme ??= const DividerThemeData();
|
||||
drawerTheme ??= const DrawerThemeData();
|
||||
elevatedButtonTheme ??= const ElevatedButtonThemeData();
|
||||
filledButtonTheme ??= const FilledButtonThemeData();
|
||||
floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
|
||||
iconButtonTheme ??= const IconButtonThemeData();
|
||||
listTileTheme ??= const ListTileThemeData();
|
||||
@ -655,6 +658,7 @@ class ThemeData with Diagnosticable {
|
||||
drawerTheme: drawerTheme,
|
||||
elevatedButtonTheme: elevatedButtonTheme,
|
||||
expansionTileTheme: expansionTileTheme,
|
||||
filledButtonTheme: filledButtonTheme,
|
||||
floatingActionButtonTheme: floatingActionButtonTheme,
|
||||
iconButtonTheme: iconButtonTheme,
|
||||
listTileTheme: listTileTheme,
|
||||
@ -761,6 +765,7 @@ class ThemeData with Diagnosticable {
|
||||
required this.drawerTheme,
|
||||
required this.elevatedButtonTheme,
|
||||
required this.expansionTileTheme,
|
||||
required this.filledButtonTheme,
|
||||
required this.floatingActionButtonTheme,
|
||||
required this.iconButtonTheme,
|
||||
required this.listTileTheme,
|
||||
@ -909,6 +914,7 @@ class ThemeData with Diagnosticable {
|
||||
assert(drawerTheme != null),
|
||||
assert(elevatedButtonTheme != null),
|
||||
assert(expansionTileTheme != null),
|
||||
assert(filledButtonTheme != null),
|
||||
assert(floatingActionButtonTheme != null),
|
||||
assert(iconButtonTheme != null),
|
||||
assert(listTileTheme != null),
|
||||
@ -1211,7 +1217,7 @@ class ThemeData with Diagnosticable {
|
||||
/// * Typography: `typography` (see table above)
|
||||
///
|
||||
/// ### Components
|
||||
/// * Common buttons: [TextButton], [OutlinedButton], [ElevatedButton]
|
||||
/// * Common buttons: [ElevatedButton], [FilledButton], [OutlinedButton], [TextButton]
|
||||
/// * FAB: [FloatingActionButton]
|
||||
/// * Extended FAB: [FloatingActionButton.extended]
|
||||
/// * Cards: [Card]
|
||||
@ -1464,6 +1470,10 @@ class ThemeData with Diagnosticable {
|
||||
/// A theme for customizing the visual properties of [ExpansionTile]s.
|
||||
final ExpansionTileThemeData expansionTileTheme;
|
||||
|
||||
/// A theme for customizing the appearance and internal layout of
|
||||
/// [FilledButton]s.
|
||||
final FilledButtonThemeData filledButtonTheme;
|
||||
|
||||
/// A theme for customizing the shape, elevation, and color of a
|
||||
/// [FloatingActionButton].
|
||||
final FloatingActionButtonThemeData floatingActionButtonTheme;
|
||||
@ -1739,6 +1749,7 @@ class ThemeData with Diagnosticable {
|
||||
DrawerThemeData? drawerTheme,
|
||||
ElevatedButtonThemeData? elevatedButtonTheme,
|
||||
ExpansionTileThemeData? expansionTileTheme,
|
||||
FilledButtonThemeData? filledButtonTheme,
|
||||
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
||||
IconButtonThemeData? iconButtonTheme,
|
||||
ListTileThemeData? listTileTheme,
|
||||
@ -1884,6 +1895,7 @@ class ThemeData with Diagnosticable {
|
||||
drawerTheme: drawerTheme ?? this.drawerTheme,
|
||||
elevatedButtonTheme: elevatedButtonTheme ?? this.elevatedButtonTheme,
|
||||
expansionTileTheme: expansionTileTheme ?? this.expansionTileTheme,
|
||||
filledButtonTheme: filledButtonTheme ?? this.filledButtonTheme,
|
||||
floatingActionButtonTheme: floatingActionButtonTheme ?? this.floatingActionButtonTheme,
|
||||
iconButtonTheme: iconButtonTheme ?? this.iconButtonTheme,
|
||||
listTileTheme: listTileTheme ?? this.listTileTheme,
|
||||
@ -2083,6 +2095,7 @@ class ThemeData with Diagnosticable {
|
||||
drawerTheme: DrawerThemeData.lerp(a.drawerTheme, b.drawerTheme, t)!,
|
||||
elevatedButtonTheme: ElevatedButtonThemeData.lerp(a.elevatedButtonTheme, b.elevatedButtonTheme, t)!,
|
||||
expansionTileTheme: ExpansionTileThemeData.lerp(a.expansionTileTheme, b.expansionTileTheme, t)!,
|
||||
filledButtonTheme: FilledButtonThemeData.lerp(a.filledButtonTheme, b.filledButtonTheme, t)!,
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData.lerp(a.floatingActionButtonTheme, b.floatingActionButtonTheme, t)!,
|
||||
iconButtonTheme: IconButtonThemeData.lerp(a.iconButtonTheme, b.iconButtonTheme, t)!,
|
||||
listTileTheme: ListTileThemeData.lerp(a.listTileTheme, b.listTileTheme, t)!,
|
||||
@ -2184,6 +2197,7 @@ class ThemeData with Diagnosticable {
|
||||
other.drawerTheme == drawerTheme &&
|
||||
other.elevatedButtonTheme == elevatedButtonTheme &&
|
||||
other.expansionTileTheme == expansionTileTheme &&
|
||||
other.filledButtonTheme == filledButtonTheme &&
|
||||
other.floatingActionButtonTheme == floatingActionButtonTheme &&
|
||||
other.iconButtonTheme == iconButtonTheme &&
|
||||
other.listTileTheme == listTileTheme &&
|
||||
@ -2282,6 +2296,7 @@ class ThemeData with Diagnosticable {
|
||||
drawerTheme,
|
||||
elevatedButtonTheme,
|
||||
expansionTileTheme,
|
||||
filledButtonTheme,
|
||||
floatingActionButtonTheme,
|
||||
iconButtonTheme,
|
||||
listTileTheme,
|
||||
@ -2382,6 +2397,7 @@ class ThemeData with Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<DrawerThemeData>('drawerTheme', drawerTheme, defaultValue: defaultData.drawerTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ElevatedButtonThemeData>('elevatedButtonTheme', elevatedButtonTheme, defaultValue: defaultData.elevatedButtonTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ExpansionTileThemeData>('expansionTileTheme', expansionTileTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<FilledButtonThemeData>('filledButtonTheme', filledButtonTheme, defaultValue: defaultData.filledButtonTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<FloatingActionButtonThemeData>('floatingActionButtonTheme', floatingActionButtonTheme, defaultValue: defaultData.floatingActionButtonTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<IconButtonThemeData>('iconButtonTheme', iconButtonTheme, defaultValue: defaultData.iconButtonTheme, level: DiagnosticLevel.debug));
|
||||
properties.add(DiagnosticsProperty<ListTileThemeData>('listTileTheme', listTileTheme, defaultValue: defaultData.listTileTheme, level: DiagnosticLevel.debug));
|
||||
|
1752
packages/flutter/test/material/filled_button_test.dart
Normal file
1752
packages/flutter/test/material/filled_button_test.dart
Normal file
File diff suppressed because it is too large
Load Diff
253
packages/flutter/test/material/filled_button_theme_test.dart
Normal file
253
packages/flutter/test/material/filled_button_theme_test.dart
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright 2014 The Flutter 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_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Passing no FilledButtonTheme returns defaults', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData.from(colorScheme: colorScheme),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: FilledButton(
|
||||
onPressed: () { },
|
||||
child: const Text('button'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder buttonMaterial = find.descendant(
|
||||
of: find.byType(FilledButton),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
|
||||
final Material material = tester.widget<Material>(buttonMaterial);
|
||||
expect(material.animationDuration, const Duration(milliseconds: 200));
|
||||
expect(material.borderRadius, null);
|
||||
expect(material.color, colorScheme.primary);
|
||||
expect(material.elevation, 0);
|
||||
expect(material.shadowColor, const Color(0xff000000));
|
||||
expect(material.shape, const StadiumBorder());
|
||||
expect(material.textStyle!.color, colorScheme.onPrimary);
|
||||
expect(material.textStyle!.fontFamily, 'Roboto');
|
||||
expect(material.textStyle!.fontSize, 14);
|
||||
expect(material.textStyle!.fontWeight, FontWeight.w500);
|
||||
|
||||
final Align align = tester.firstWidget<Align>(find.ancestor(of: find.text('button'), matching: find.byType(Align)));
|
||||
expect(align.alignment, Alignment.center);
|
||||
});
|
||||
|
||||
group('[Theme, TextTheme, FilledButton style overrides]', () {
|
||||
const Color foregroundColor = Color(0xff000001);
|
||||
const Color backgroundColor = Color(0xff000002);
|
||||
const Color disabledForegroundColor = Color(0xff000003);
|
||||
const Color disabledBackgroundColor = Color(0xff000004);
|
||||
const Color shadowColor = Color(0xff000005);
|
||||
const double elevation = 1;
|
||||
const TextStyle textStyle = TextStyle(fontSize: 12.0);
|
||||
const EdgeInsets padding = EdgeInsets.all(3);
|
||||
const Size minimumSize = Size(200, 200);
|
||||
const BorderSide side = BorderSide(color: Colors.green, width: 2);
|
||||
const OutlinedBorder shape = RoundedRectangleBorder(side: side, borderRadius: BorderRadius.all(Radius.circular(2)));
|
||||
const MouseCursor enabledMouseCursor = SystemMouseCursors.text;
|
||||
const MouseCursor disabledMouseCursor = SystemMouseCursors.grab;
|
||||
const MaterialTapTargetSize tapTargetSize = MaterialTapTargetSize.shrinkWrap;
|
||||
const Duration animationDuration = Duration(milliseconds: 25);
|
||||
const bool enableFeedback = false;
|
||||
const AlignmentGeometry alignment = Alignment.centerLeft;
|
||||
|
||||
final ButtonStyle style = FilledButton.styleFrom(
|
||||
foregroundColor: foregroundColor,
|
||||
backgroundColor: backgroundColor,
|
||||
disabledForegroundColor: disabledForegroundColor,
|
||||
disabledBackgroundColor: disabledBackgroundColor,
|
||||
shadowColor: shadowColor,
|
||||
elevation: elevation,
|
||||
textStyle: textStyle,
|
||||
padding: padding,
|
||||
minimumSize: minimumSize,
|
||||
side: side,
|
||||
shape: shape,
|
||||
enabledMouseCursor: enabledMouseCursor,
|
||||
disabledMouseCursor: disabledMouseCursor,
|
||||
tapTargetSize: tapTargetSize,
|
||||
animationDuration: animationDuration,
|
||||
enableFeedback: enableFeedback,
|
||||
alignment: alignment,
|
||||
);
|
||||
|
||||
Widget buildFrame({ ButtonStyle? buttonStyle, ButtonStyle? themeStyle, ButtonStyle? overallStyle }) {
|
||||
final Widget child = Builder(
|
||||
builder: (BuildContext context) {
|
||||
return FilledButton(
|
||||
style: buttonStyle,
|
||||
onPressed: () { },
|
||||
child: const Text('button'),
|
||||
);
|
||||
},
|
||||
);
|
||||
return MaterialApp(
|
||||
theme: ThemeData.from(colorScheme: const ColorScheme.light()).copyWith(
|
||||
filledButtonTheme: FilledButtonThemeData(style: overallStyle),
|
||||
),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
// If the FilledButtonTheme widget is present, it's used
|
||||
// instead of the Theme's ThemeData.FilledButtonTheme.
|
||||
child: themeStyle == null ? child : FilledButtonTheme(
|
||||
data: FilledButtonThemeData(style: themeStyle),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final Finder findMaterial = find.descendant(
|
||||
of: find.byType(FilledButton),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
|
||||
final Finder findInkWell = find.descendant(
|
||||
of: find.byType(FilledButton),
|
||||
matching: find.byType(InkWell),
|
||||
);
|
||||
|
||||
const Set<MaterialState> enabled = <MaterialState>{};
|
||||
const Set<MaterialState> disabled = <MaterialState>{ MaterialState.disabled };
|
||||
const Set<MaterialState> hovered = <MaterialState>{ MaterialState.hovered };
|
||||
const Set<MaterialState> focused = <MaterialState>{ MaterialState.focused };
|
||||
const Set<MaterialState> pressed = <MaterialState>{ MaterialState.pressed };
|
||||
|
||||
void checkButton(WidgetTester tester) {
|
||||
final Material material = tester.widget<Material>(findMaterial);
|
||||
final InkWell inkWell = tester.widget<InkWell>(findInkWell);
|
||||
expect(material.textStyle!.color, foregroundColor);
|
||||
expect(material.textStyle!.fontSize, 12);
|
||||
expect(material.color, backgroundColor);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
expect(material.elevation, elevation);
|
||||
expect(MaterialStateProperty.resolveAs<MouseCursor>(inkWell.mouseCursor!, enabled), enabledMouseCursor);
|
||||
expect(MaterialStateProperty.resolveAs<MouseCursor>(inkWell.mouseCursor!, disabled), disabledMouseCursor);
|
||||
expect(inkWell.overlayColor!.resolve(hovered), foregroundColor.withOpacity(0.08));
|
||||
expect(inkWell.overlayColor!.resolve(focused), foregroundColor.withOpacity(0.12));
|
||||
expect(inkWell.overlayColor!.resolve(pressed), foregroundColor.withOpacity(0.12));
|
||||
expect(inkWell.enableFeedback, enableFeedback);
|
||||
expect(material.borderRadius, null);
|
||||
expect(material.shape, shape);
|
||||
expect(material.animationDuration, animationDuration);
|
||||
expect(tester.getSize(find.byType(FilledButton)), const Size(200, 200));
|
||||
final Align align = tester.firstWidget<Align>(find.ancestor(of: find.text('button'), matching: find.byType(Align)));
|
||||
expect(align.alignment, alignment);
|
||||
}
|
||||
|
||||
testWidgets('Button style overrides defaults', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(buttonStyle: style));
|
||||
await tester.pumpAndSettle(); // allow the animations to finish
|
||||
checkButton(tester);
|
||||
});
|
||||
|
||||
testWidgets('Button theme style overrides defaults', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(themeStyle: style));
|
||||
await tester.pumpAndSettle();
|
||||
checkButton(tester);
|
||||
});
|
||||
|
||||
testWidgets('Overall Theme button theme style overrides defaults', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(overallStyle: style));
|
||||
await tester.pumpAndSettle();
|
||||
checkButton(tester);
|
||||
});
|
||||
|
||||
// Same as the previous tests with empty ButtonStyle's instead of null.
|
||||
|
||||
testWidgets('Button style overrides defaults, empty theme and overall styles', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(buttonStyle: style, themeStyle: const ButtonStyle(), overallStyle: const ButtonStyle()));
|
||||
await tester.pumpAndSettle(); // allow the animations to finish
|
||||
checkButton(tester);
|
||||
});
|
||||
|
||||
testWidgets('Button theme style overrides defaults, empty button and overall styles', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(buttonStyle: const ButtonStyle(), themeStyle: style, overallStyle: const ButtonStyle()));
|
||||
await tester.pumpAndSettle(); // allow the animations to finish
|
||||
checkButton(tester);
|
||||
});
|
||||
|
||||
testWidgets('Overall Theme button theme style overrides defaults, null theme and empty overall style', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(buildFrame(buttonStyle: const ButtonStyle(), overallStyle: style));
|
||||
await tester.pumpAndSettle(); // allow the animations to finish
|
||||
checkButton(tester);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Theme shadowColor', (WidgetTester tester) async {
|
||||
const ColorScheme colorScheme = ColorScheme.light();
|
||||
const Color shadowColor = Color(0xff000001);
|
||||
const Color overriddenColor = Color(0xff000002);
|
||||
|
||||
Widget buildFrame({ Color? overallShadowColor, Color? themeShadowColor, Color? shadowColor }) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData.from(colorScheme: colorScheme).copyWith(
|
||||
shadowColor: overallShadowColor,
|
||||
),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: FilledButtonTheme(
|
||||
data: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
shadowColor: themeShadowColor,
|
||||
),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
shadowColor: shadowColor,
|
||||
),
|
||||
onPressed: () { },
|
||||
child: const Text('button'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final Finder buttonMaterialFinder = find.descendant(
|
||||
of: find.byType(FilledButton),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(buildFrame());
|
||||
Material material = tester.widget<Material>(buttonMaterialFinder);
|
||||
expect(material.shadowColor, Colors.black); //default
|
||||
|
||||
await tester.pumpWidget(buildFrame(themeShadowColor: shadowColor));
|
||||
await tester.pumpAndSettle(); // theme animation
|
||||
material = tester.widget<Material>(buttonMaterialFinder);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(shadowColor: shadowColor));
|
||||
await tester.pumpAndSettle(); // theme animation
|
||||
material = tester.widget<Material>(buttonMaterialFinder);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(overallShadowColor: overriddenColor, themeShadowColor: shadowColor));
|
||||
await tester.pumpAndSettle(); // theme animation
|
||||
material = tester.widget<Material>(buttonMaterialFinder);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
|
||||
await tester.pumpWidget(buildFrame(themeShadowColor: overriddenColor, shadowColor: shadowColor));
|
||||
await tester.pumpAndSettle(); // theme animation
|
||||
material = tester.widget<Material>(buttonMaterialFinder);
|
||||
expect(material.shadowColor, shadowColor);
|
||||
});
|
||||
}
|
@ -695,6 +695,7 @@ void main() {
|
||||
drawerTheme: const DrawerThemeData(),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(style: ElevatedButton.styleFrom(backgroundColor: Colors.green)),
|
||||
expansionTileTheme: const ExpansionTileThemeData(backgroundColor: Colors.black),
|
||||
filledButtonTheme: FilledButtonThemeData(style: FilledButton.styleFrom(foregroundColor: Colors.green)),
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.black),
|
||||
iconButtonTheme: IconButtonThemeData(style: IconButton.styleFrom(foregroundColor: Colors.pink)),
|
||||
listTileTheme: const ListTileThemeData(),
|
||||
@ -808,6 +809,7 @@ void main() {
|
||||
drawerTheme: const DrawerThemeData(),
|
||||
elevatedButtonTheme: const ElevatedButtonThemeData(),
|
||||
expansionTileTheme: const ExpansionTileThemeData(backgroundColor: Colors.black),
|
||||
filledButtonTheme: const FilledButtonThemeData(),
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.white),
|
||||
iconButtonTheme: const IconButtonThemeData(),
|
||||
listTileTheme: const ListTileThemeData(),
|
||||
@ -907,6 +909,7 @@ void main() {
|
||||
drawerTheme: otherTheme.drawerTheme,
|
||||
elevatedButtonTheme: otherTheme.elevatedButtonTheme,
|
||||
expansionTileTheme: otherTheme.expansionTileTheme,
|
||||
filledButtonTheme: otherTheme.filledButtonTheme,
|
||||
floatingActionButtonTheme: otherTheme.floatingActionButtonTheme,
|
||||
iconButtonTheme: otherTheme.iconButtonTheme,
|
||||
listTileTheme: otherTheme.listTileTheme,
|
||||
@ -1005,6 +1008,7 @@ void main() {
|
||||
expect(themeDataCopy.drawerTheme, equals(otherTheme.drawerTheme));
|
||||
expect(themeDataCopy.elevatedButtonTheme, equals(otherTheme.elevatedButtonTheme));
|
||||
expect(themeDataCopy.expansionTileTheme, equals(otherTheme.expansionTileTheme));
|
||||
expect(themeDataCopy.filledButtonTheme, equals(otherTheme.filledButtonTheme));
|
||||
expect(themeDataCopy.floatingActionButtonTheme, equals(otherTheme.floatingActionButtonTheme));
|
||||
expect(themeDataCopy.iconButtonTheme, equals(otherTheme.iconButtonTheme));
|
||||
expect(themeDataCopy.listTileTheme, equals(otherTheme.listTileTheme));
|
||||
@ -1140,6 +1144,7 @@ void main() {
|
||||
'dividerTheme',
|
||||
'drawerTheme',
|
||||
'elevatedButtonTheme',
|
||||
'filledButtonTheme',
|
||||
'floatingActionButtonTheme',
|
||||
'iconButtonTheme',
|
||||
'listTileTheme',
|
||||
|
Loading…
x
Reference in New Issue
Block a user