[Material] Support for hovered, focused, and pressed border color on OutlineButton
s (#34872)
* outline border implements material state property
This commit is contained in:
parent
d780c2cff6
commit
3f94159989
@ -87,7 +87,7 @@ class RawMaterialButton extends StatefulWidget {
|
|||||||
/// Defines the default text style, with [Material.textStyle], for the
|
/// Defines the default text style, with [Material.textStyle], for the
|
||||||
/// button's [child].
|
/// button's [child].
|
||||||
///
|
///
|
||||||
/// If [textStyle.color] is a [MaterialStateColor], [MaterialStateColor.resolveColor]
|
/// If [textStyle.color] is a [MaterialStateProperty<Color>], [MaterialStateProperty.resolve]
|
||||||
/// is used for the following [MaterialState]s:
|
/// is used for the following [MaterialState]s:
|
||||||
///
|
///
|
||||||
/// * [MaterialState.pressed].
|
/// * [MaterialState.pressed].
|
||||||
@ -199,6 +199,14 @@ class RawMaterialButton extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
/// The button's highlight and splash are clipped to this shape. If the
|
/// The button's highlight and splash are clipped to this shape. If the
|
||||||
/// button has an elevation, then its drop shadow is defined by this shape.
|
/// button has an elevation, then its drop shadow is defined by this shape.
|
||||||
|
///
|
||||||
|
/// If [shape] is a [MaterialStateProperty<ShapeBorder>], [MaterialStateProperty.resolve]
|
||||||
|
/// is used for the following [MaterialState]s:
|
||||||
|
///
|
||||||
|
/// * [MaterialState.pressed].
|
||||||
|
/// * [MaterialState.hovered].
|
||||||
|
/// * [MaterialState.focused].
|
||||||
|
/// * [MaterialState.disabled].
|
||||||
final ShapeBorder shape;
|
final ShapeBorder shape;
|
||||||
|
|
||||||
/// Defines the duration of animated changes for [shape] and [elevation].
|
/// Defines the duration of animated changes for [shape] and [elevation].
|
||||||
@ -317,7 +325,8 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Color effectiveTextColor = MaterialStateColor.resolveColor(widget.textStyle?.color, _states);
|
final Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states);
|
||||||
|
final ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states);
|
||||||
|
|
||||||
final Widget result = Focus(
|
final Widget result = Focus(
|
||||||
focusNode: widget.focusNode,
|
focusNode: widget.focusNode,
|
||||||
@ -327,7 +336,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
|||||||
child: Material(
|
child: Material(
|
||||||
elevation: _effectiveElevation,
|
elevation: _effectiveElevation,
|
||||||
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
||||||
shape: widget.shape,
|
shape: effectiveShape,
|
||||||
color: widget.fillColor,
|
color: widget.fillColor,
|
||||||
type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button,
|
type: widget.fillColor == null ? MaterialType.transparency : MaterialType.button,
|
||||||
animationDuration: widget.animationDuration,
|
animationDuration: widget.animationDuration,
|
||||||
@ -340,7 +349,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
|||||||
hoverColor: widget.hoverColor,
|
hoverColor: widget.hoverColor,
|
||||||
onHover: _handleHoveredChanged,
|
onHover: _handleHoveredChanged,
|
||||||
onTap: widget.onPressed,
|
onTap: widget.onPressed,
|
||||||
customBorder: widget.shape,
|
customBorder: effectiveShape,
|
||||||
child: IconTheme.merge(
|
child: IconTheme.merge(
|
||||||
data: IconThemeData(color: effectiveTextColor),
|
data: IconThemeData(color: effectiveTextColor),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -481,11 +481,10 @@ class ButtonThemeData extends Diagnosticable {
|
|||||||
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
|
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
|
||||||
/// with its opacity set to 0.30 if [getBrightness] is dark, 0.38 otherwise.
|
/// with its opacity set to 0.30 if [getBrightness] is dark, 0.38 otherwise.
|
||||||
///
|
///
|
||||||
/// If [MaterialButton.textColor] is a [MaterialStateColor], it will be used
|
/// If [MaterialButton.textColor] is a [MaterialStateProperty<Color>], it will be
|
||||||
/// as the `disabledTextColor`. It will be resolved in the
|
/// used as the `disabledTextColor`. It will be resolved in the [MaterialState.disabled] state.
|
||||||
/// [MaterialState.disabled] state.
|
|
||||||
Color getDisabledTextColor(MaterialButton button) {
|
Color getDisabledTextColor(MaterialButton button) {
|
||||||
if (button.textColor is MaterialStateColor)
|
if (button.textColor is MaterialStateProperty<Color>)
|
||||||
return button.textColor;
|
return button.textColor;
|
||||||
if (button.disabledTextColor != null)
|
if (button.disabledTextColor != null)
|
||||||
return button.disabledTextColor;
|
return button.disabledTextColor;
|
||||||
|
@ -100,8 +100,8 @@ class MaterialButton extends StatelessWidget {
|
|||||||
/// The default text color depends on the button theme's text theme,
|
/// The default text color depends on the button theme's text theme,
|
||||||
/// [ButtonThemeData.textTheme].
|
/// [ButtonThemeData.textTheme].
|
||||||
///
|
///
|
||||||
/// If [textColor] is a [MaterialStateColor], [disabledTextColor] will be
|
/// If [textColor] is a [MaterialStateProperty<Color>], [disabledTextColor]
|
||||||
/// ignored.
|
/// will be ignored.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
@ -117,8 +117,8 @@ class MaterialButton extends StatelessWidget {
|
|||||||
/// The default value is the theme's disabled color,
|
/// The default value is the theme's disabled color,
|
||||||
/// [ThemeData.disabledColor].
|
/// [ThemeData.disabledColor].
|
||||||
///
|
///
|
||||||
/// If [textColor] is a [MaterialStateColor], [disabledTextColor] will be
|
/// If [textColor] is a [MaterialStateProperty<Color>], [disabledTextColor]
|
||||||
/// ignored.
|
/// will be ignored.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
@ -62,8 +62,9 @@ enum MaterialState {
|
|||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signature for the function that returns a color based on a given set of states.
|
/// Signature for the function that returns a value of type `T` based on a given
|
||||||
typedef MaterialStateColorResolver = Color Function(Set<MaterialState> states);
|
/// set of states.
|
||||||
|
typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);
|
||||||
|
|
||||||
/// Defines a [Color] whose value depends on a set of [MaterialState]s which
|
/// Defines a [Color] whose value depends on a set of [MaterialState]s which
|
||||||
/// represent the interactive state of a component.
|
/// represent the interactive state of a component.
|
||||||
@ -109,7 +110,7 @@ typedef MaterialStateColorResolver = Color Function(Set<MaterialState> states);
|
|||||||
/// ),
|
/// ),
|
||||||
/// ```
|
/// ```
|
||||||
/// {@end-tool}
|
/// {@end-tool}
|
||||||
abstract class MaterialStateColor extends Color {
|
abstract class MaterialStateColor extends Color implements MaterialStateProperty<Color> {
|
||||||
/// Creates a [MaterialStateColor].
|
/// Creates a [MaterialStateColor].
|
||||||
///
|
///
|
||||||
/// If you want a `const` [MaterialStateColor], you'll need to extend
|
/// If you want a `const` [MaterialStateColor], you'll need to extend
|
||||||
@ -141,33 +142,24 @@ abstract class MaterialStateColor extends Color {
|
|||||||
/// {@end-tool}
|
/// {@end-tool}
|
||||||
const MaterialStateColor(int defaultValue) : super(defaultValue);
|
const MaterialStateColor(int defaultValue) : super(defaultValue);
|
||||||
|
|
||||||
/// Creates a [MaterialStateColor] from a [MaterialStateColorResolver] callback function.
|
/// Creates a [MaterialStateColor] from a [MaterialPropertyResolver<Color>]
|
||||||
|
/// callback function.
|
||||||
///
|
///
|
||||||
/// If used as a regular color, the color resolved in the default state (the
|
/// If used as a regular color, the color resolved in the default state (the
|
||||||
/// empty set of states) will be used.
|
/// empty set of states) will be used.
|
||||||
///
|
///
|
||||||
/// The given callback parameter must return a non-null color in the default
|
/// The given callback parameter must return a non-null color in the default
|
||||||
/// state.
|
/// state.
|
||||||
factory MaterialStateColor.resolveWith(MaterialStateColorResolver callback) => _MaterialStateColor(callback);
|
static MaterialStateColor resolveWith(MaterialPropertyResolver<Color> callback) => _MaterialStateColor(callback);
|
||||||
|
|
||||||
/// Returns a [Color] that's to be used when a Material component is in the
|
/// Returns a [Color] that's to be used when a Material component is in the
|
||||||
/// specified state.
|
/// specified state.
|
||||||
|
@override
|
||||||
Color resolve(Set<MaterialState> states);
|
Color resolve(Set<MaterialState> states);
|
||||||
|
|
||||||
/// Returns the color for the given set of states if `color` is a
|
|
||||||
/// [MaterialStateColor], otherwise returns the color itself.
|
|
||||||
///
|
|
||||||
/// This is useful for widgets that have parameters which can be [Color] or
|
|
||||||
/// [MaterialStateColor] values.
|
|
||||||
static Color resolveColor(Color color, Set<MaterialState> states) {
|
|
||||||
if (color is MaterialStateColor) {
|
|
||||||
return color.resolve(states);
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [MaterialStateColor] created from a [MaterialStateColorResolver] callback alone.
|
/// A [MaterialStateColor] created from a [MaterialPropertyResolver<Color>]
|
||||||
|
/// callback alone.
|
||||||
///
|
///
|
||||||
/// If used as a regular color, the color resolved in the default state will
|
/// If used as a regular color, the color resolved in the default state will
|
||||||
/// be used.
|
/// be used.
|
||||||
@ -176,7 +168,7 @@ abstract class MaterialStateColor extends Color {
|
|||||||
class _MaterialStateColor extends MaterialStateColor {
|
class _MaterialStateColor extends MaterialStateColor {
|
||||||
_MaterialStateColor(this._resolve) : super(_resolve(_defaultStates).value);
|
_MaterialStateColor(this._resolve) : super(_resolve(_defaultStates).value);
|
||||||
|
|
||||||
final MaterialStateColorResolver _resolve;
|
final MaterialPropertyResolver<Color> _resolve;
|
||||||
|
|
||||||
/// The default state for a Material component, the empty set of interaction states.
|
/// The default state for a Material component, the empty set of interaction states.
|
||||||
static const Set<MaterialState> _defaultStates = <MaterialState>{};
|
static const Set<MaterialState> _defaultStates = <MaterialState>{};
|
||||||
@ -184,3 +176,46 @@ class _MaterialStateColor extends MaterialStateColor {
|
|||||||
@override
|
@override
|
||||||
Color resolve(Set<MaterialState> states) => _resolve(states);
|
Color resolve(Set<MaterialState> states) => _resolve(states);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interface for classes that can return a value of type `T` based on a set of
|
||||||
|
/// [MaterialState]s.
|
||||||
|
///
|
||||||
|
/// For example, [MaterialStateColor] implements `MaterialStateProperty<Color>`
|
||||||
|
/// because it has a `resolve` method that returns a different [Color] depending
|
||||||
|
/// on the given set of [MaterialState]s.
|
||||||
|
abstract class MaterialStateProperty<T> {
|
||||||
|
|
||||||
|
/// Returns a different value of type `T` depending on the given `states`.
|
||||||
|
///
|
||||||
|
/// Some widgets (such as [RawMaterialButton]) keep track of their set of
|
||||||
|
/// [MaterialState]s, and will call `resolve` with the current states at build
|
||||||
|
/// time for specified properties (such as [RawMaterialButton.textStyle]'s color).
|
||||||
|
T resolve(Set<MaterialState> states);
|
||||||
|
|
||||||
|
/// Returns the value resolved in the given set of states if `value` is a
|
||||||
|
/// [MaterialStateProperty], otherwise returns the value itself.
|
||||||
|
///
|
||||||
|
/// This is useful for widgets that have parameters which can optionally be a
|
||||||
|
/// [MaterialStateProperty]. For example, [RaisedButton.textColor] can be a
|
||||||
|
/// [Color] or a [MaterialStateProperty<Color>].
|
||||||
|
static T resolveAs<T>(T value, Set<MaterialState> states) {
|
||||||
|
if (value is MaterialStateProperty<T>) {
|
||||||
|
final MaterialStateProperty<T> property = value;
|
||||||
|
return property.resolve(states);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method for creating a [MaterialStateProperty] from a
|
||||||
|
/// [MaterialPropertyResolver] function alone.
|
||||||
|
static MaterialStateProperty<T> resolveWith<T>(MaterialPropertyResolver<T> callback) => _MaterialStateProperty<T>(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MaterialStateProperty<T> implements MaterialStateProperty<T> {
|
||||||
|
_MaterialStateProperty(this._resolve);
|
||||||
|
|
||||||
|
final MaterialPropertyResolver<T> _resolve;
|
||||||
|
|
||||||
|
@override
|
||||||
|
T resolve(Set<MaterialState> states) => _resolve(states);
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'button_theme.dart';
|
import 'button_theme.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'material_button.dart';
|
import 'material_button.dart';
|
||||||
|
import 'material_state.dart';
|
||||||
import 'raised_button.dart';
|
import 'raised_button.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
@ -132,12 +133,16 @@ class OutlineButton extends MaterialButton {
|
|||||||
///
|
///
|
||||||
/// By default the border's color does not change when the button
|
/// By default the border's color does not change when the button
|
||||||
/// is pressed.
|
/// is pressed.
|
||||||
|
///
|
||||||
|
/// This field is ignored if [borderSide.color] is a [MaterialStateProperty<Color>].
|
||||||
final Color highlightedBorderColor;
|
final Color highlightedBorderColor;
|
||||||
|
|
||||||
/// The outline border's color when the button is not [enabled].
|
/// The outline border's color when the button is not [enabled].
|
||||||
///
|
///
|
||||||
/// By default the outline border's color does not change when the
|
/// By default the outline border's color does not change when the
|
||||||
/// button is disabled.
|
/// button is disabled.
|
||||||
|
///
|
||||||
|
/// This field is ignored if [borderSide.color] is a [MaterialStateProperty<Color>].
|
||||||
final Color disabledBorderColor;
|
final Color disabledBorderColor;
|
||||||
|
|
||||||
/// Defines the color of the border when the button is enabled but not
|
/// Defines the color of the border when the button is enabled but not
|
||||||
@ -148,6 +153,10 @@ class OutlineButton extends MaterialButton {
|
|||||||
///
|
///
|
||||||
/// If null the default border's style is [BorderStyle.solid], its
|
/// If null the default border's style is [BorderStyle.solid], its
|
||||||
/// [BorderSide.width] is 1.0, and its color is a light shade of grey.
|
/// [BorderSide.width] is 1.0, and its color is a light shade of grey.
|
||||||
|
///
|
||||||
|
/// If [borderSide.color] is a [MaterialStateProperty<Color>], [MaterialStateProperty.resolve]
|
||||||
|
/// is used in all states and both [highlightedBorderColor] and [disabledBorderColor]
|
||||||
|
/// are ignored.
|
||||||
final BorderSide borderSide;
|
final BorderSide borderSide;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -370,18 +379,26 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
|||||||
return colorTween.evaluate(_fillAnimation);
|
return colorTween.evaluate(_fillAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color get _outlineColor {
|
||||||
|
// If outline color is a `MaterialStateProperty`, it will be used in all
|
||||||
|
// states, otherwise we determine the outline color in the current state.
|
||||||
|
if (widget.borderSide?.color is MaterialStateProperty<Color>)
|
||||||
|
return widget.borderSide.color;
|
||||||
|
if (!widget.enabled)
|
||||||
|
return widget.disabledBorderColor;
|
||||||
|
if (_pressed)
|
||||||
|
return widget.highlightedBorderColor;
|
||||||
|
return widget.borderSide?.color;
|
||||||
|
}
|
||||||
|
|
||||||
BorderSide _getOutline() {
|
BorderSide _getOutline() {
|
||||||
if (widget.borderSide?.style == BorderStyle.none)
|
if (widget.borderSide?.style == BorderStyle.none)
|
||||||
return widget.borderSide;
|
return widget.borderSide;
|
||||||
|
|
||||||
final Color specifiedColor = widget.enabled
|
|
||||||
? (_pressed ? widget.highlightedBorderColor : null) ?? widget.borderSide?.color
|
|
||||||
: widget.disabledBorderColor;
|
|
||||||
|
|
||||||
final Color themeColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.12);
|
final Color themeColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.12);
|
||||||
|
|
||||||
return BorderSide(
|
return BorderSide(
|
||||||
color: specifiedColor ?? themeColor,
|
color: _outlineColor ?? themeColor,
|
||||||
width: widget.borderSide?.width ?? 1.0,
|
width: widget.borderSide?.width ?? 1.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -433,7 +450,7 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
|||||||
|
|
||||||
// Render the button's outline border using using the OutlineButton's
|
// Render the button's outline border using using the OutlineButton's
|
||||||
// border parameters and the button or buttonTheme's shape.
|
// border parameters and the button or buttonTheme's shape.
|
||||||
class _OutlineBorder extends ShapeBorder {
|
class _OutlineBorder extends ShapeBorder implements MaterialStateProperty<ShapeBorder>{
|
||||||
const _OutlineBorder({
|
const _OutlineBorder({
|
||||||
@required this.shape,
|
@required this.shape,
|
||||||
@required this.side,
|
@required this.side,
|
||||||
@ -512,4 +529,12 @@ class _OutlineBorder extends ShapeBorder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashValues(side, shape);
|
int get hashCode => hashValues(side, shape);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder resolve(Set<MaterialState> states) {
|
||||||
|
return _OutlineBorder(
|
||||||
|
shape: shape,
|
||||||
|
side: side.copyWith(color: MaterialStateProperty.resolveAs<Color>(side.color, states),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,6 +318,138 @@ void main() {
|
|||||||
expect(textColor(), isNot(unusedDisabledTextColor));
|
expect(textColor(), isNot(unusedDisabledTextColor));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('OutlineButton uses stateful color for border color in different states', (WidgetTester tester) async {
|
||||||
|
final FocusNode focusNode = FocusNode();
|
||||||
|
|
||||||
|
const Color pressedColor = Color(1);
|
||||||
|
const Color hoverColor = Color(2);
|
||||||
|
const Color focusedColor = Color(3);
|
||||||
|
const Color defaultColor = Color(4);
|
||||||
|
|
||||||
|
Color getBorderColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.pressed)) {
|
||||||
|
return pressedColor;
|
||||||
|
}
|
||||||
|
if (states.contains(MaterialState.hovered)) {
|
||||||
|
return hoverColor;
|
||||||
|
}
|
||||||
|
if (states.contains(MaterialState.focused)) {
|
||||||
|
return focusedColor;
|
||||||
|
}
|
||||||
|
return defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
child: const Text('OutlineButton'),
|
||||||
|
onPressed: () {},
|
||||||
|
focusNode: focusNode,
|
||||||
|
borderSide: BorderSide(color: MaterialStateColor.resolveWith(getBorderColor)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Finder outlineButton = find.byType(OutlineButton);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
expect(outlineButton, paints..path(color: defaultColor));
|
||||||
|
|
||||||
|
// Focused.
|
||||||
|
focusNode.requestFocus();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(outlineButton, paints..path(color: focusedColor));
|
||||||
|
|
||||||
|
// Hovered.
|
||||||
|
final Offset center = tester.getCenter(find.byType(OutlineButton));
|
||||||
|
final TestGesture gesture = await tester.createGesture(
|
||||||
|
kind: PointerDeviceKind.mouse,
|
||||||
|
);
|
||||||
|
await gesture.addPointer();
|
||||||
|
await gesture.moveTo(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(outlineButton, paints..path(color: hoverColor));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
await gesture.down(center);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(outlineButton, paints..path(color: pressedColor));
|
||||||
|
await gesture.removePointer();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('OutlineButton ignores highlightBorderColor if border color is stateful', (WidgetTester tester) async {
|
||||||
|
const Color pressedColor = Color(1);
|
||||||
|
const Color defaultColor = Color(2);
|
||||||
|
const Color ignoredPressedColor = Color(3);
|
||||||
|
|
||||||
|
Color getBorderColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.pressed)) {
|
||||||
|
return pressedColor;
|
||||||
|
}
|
||||||
|
return defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
child: const Text('OutlineButton'),
|
||||||
|
onPressed: () {},
|
||||||
|
borderSide: BorderSide(color: MaterialStateColor.resolveWith(getBorderColor)),
|
||||||
|
highlightedBorderColor: ignoredPressedColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Finder outlineButton = find.byType(OutlineButton);
|
||||||
|
|
||||||
|
// Default, not disabled.
|
||||||
|
expect(outlineButton, paints..path(color: defaultColor));
|
||||||
|
|
||||||
|
// Highlighted (pressed).
|
||||||
|
await tester.press(outlineButton);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(outlineButton, paints..path(color: pressedColor));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('OutlineButton ignores disabledBorderColor if border color is stateful', (WidgetTester tester) async {
|
||||||
|
const Color disabledColor = Color(1);
|
||||||
|
const Color defaultColor = Color(2);
|
||||||
|
const Color ignoredDisabledColor = Color(3);
|
||||||
|
|
||||||
|
Color getBorderColor(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return disabledColor;
|
||||||
|
}
|
||||||
|
return defaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
child: const Text('OutlineButton'),
|
||||||
|
onPressed: null,
|
||||||
|
borderSide: BorderSide(color: MaterialStateColor.resolveWith(getBorderColor)),
|
||||||
|
highlightedBorderColor: ignoredDisabledColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disabled.
|
||||||
|
expect(find.byType(OutlineButton), paints..path(color: disabledColor));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Outline button responds to tap when enabled', (WidgetTester tester) async {
|
testWidgets('Outline button responds to tap when enabled', (WidgetTester tester) async {
|
||||||
int pressedCount = 0;
|
int pressedCount = 0;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user