Update OutlineButton default border width and highlight elevation (#27575)
These changes are **backwards incompatible**. Tests that verify OutlineButton visuals, for example golden image tests, will need to be updated.
This commit is contained in:
parent
5b943257d7
commit
a9c05a72f7
@ -632,16 +632,15 @@ class ButtonThemeData extends Diagnosticable {
|
|||||||
///
|
///
|
||||||
/// Returns the button's [MaterialButton.highlightElevation] if it is non-null.
|
/// Returns the button's [MaterialButton.highlightElevation] if it is non-null.
|
||||||
///
|
///
|
||||||
/// If button is a [FlatButton] then the highlight elevation is 0.0, if it's
|
/// If button is a [FlatButton] or an [OutlineButton] then the highlight
|
||||||
/// a [OutlineButton] then the highlight elevation is 2.0, otherise the
|
/// elevation is 0.0, otherise the highlight elevation is 8.0.
|
||||||
/// highlight elevation is 8.0.
|
|
||||||
double getHighlightElevation(MaterialButton button) {
|
double getHighlightElevation(MaterialButton button) {
|
||||||
if (button.highlightElevation != null)
|
if (button.highlightElevation != null)
|
||||||
return button.highlightElevation;
|
return button.highlightElevation;
|
||||||
if (button is FlatButton)
|
if (button is FlatButton)
|
||||||
return 0.0;
|
return 0.0;
|
||||||
if (button is OutlineButton)
|
if (button is OutlineButton)
|
||||||
return 2.0;
|
return 0.0;
|
||||||
return 8.0;
|
return 8.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,28 +12,31 @@ import 'raised_button.dart';
|
|||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
// The total time to make the button's fill color opaque and change
|
// The total time to make the button's fill color opaque and change
|
||||||
// its elevation.
|
// its elevation. Only applies when highlightElevation > 0.0.
|
||||||
const Duration _kPressDuration = Duration(milliseconds: 150);
|
const Duration _kPressDuration = Duration(milliseconds: 150);
|
||||||
|
|
||||||
// Half of _kPressDuration: just the time to change the button's
|
// Half of _kPressDuration: just the time to change the button's
|
||||||
// elevation.
|
// elevation. Only applies when highlightElevation > 0.0.
|
||||||
const Duration _kElevationDuration = Duration(milliseconds: 75);
|
const Duration _kElevationDuration = Duration(milliseconds: 75);
|
||||||
|
|
||||||
/// A cross between [RaisedButton] and [FlatButton]: a bordered button whose
|
/// Similar to a [FlatButton] with a thin grey rounded rectangle border.
|
||||||
/// elevation increases and whose background becomes opaque when the button
|
|
||||||
/// is pressed.
|
|
||||||
///
|
///
|
||||||
/// An outline button's elevation is initially 0.0 and its background [color]
|
/// The outline button's border shape is defined by [shape]
|
||||||
/// is transparent. When the button is pressed its background becomes opaque
|
/// and its appearance is defined by [borderSide], [disabledBorderColor],
|
||||||
/// and then its elevation increases to [highlightElevation].
|
/// and [highlightedBorderColor]. By default the border is a one pixel
|
||||||
///
|
/// wide grey rounded rectangle that does not change when the button is
|
||||||
/// The outline button has a border whose shape is defined by [shape]
|
/// pressed or disabled. By default the button's background is transparent.
|
||||||
/// and whose appearance is defined by [borderSide], [disabledBorderColor],
|
|
||||||
/// and [highlightedBorderColor].
|
|
||||||
///
|
///
|
||||||
/// If the [onPressed] callback is null, then the button will be disabled and by
|
/// If the [onPressed] callback is null, then the button will be disabled and by
|
||||||
/// default will resemble a flat button in the [disabledColor].
|
/// default will resemble a flat button in the [disabledColor].
|
||||||
///
|
///
|
||||||
|
/// The button's [highlightElevation], which defines the size of the
|
||||||
|
/// drop shadow when the button is pressed, is 0.0 (no shadow) by default.
|
||||||
|
/// If [highlightElevation] is given a value greater than 0.0 then the button
|
||||||
|
/// becomes a cross between [RaisedButton] and [FlatButton]: a bordered
|
||||||
|
/// button whose elevation increases and whose background becomes opaque
|
||||||
|
/// when the button is pressed.
|
||||||
|
///
|
||||||
/// If you want an ink-splash effect for taps, but don't want to use a button,
|
/// If you want an ink-splash effect for taps, but don't want to use a button,
|
||||||
/// consider using [InkWell] directly.
|
/// consider using [InkWell] directly.
|
||||||
///
|
///
|
||||||
@ -50,10 +53,10 @@ const Duration _kElevationDuration = Duration(milliseconds: 75);
|
|||||||
/// * [InkWell], which implements the ink splash part of a flat button.
|
/// * [InkWell], which implements the ink splash part of a flat button.
|
||||||
/// * <https://material.io/design/components/buttons.html>
|
/// * <https://material.io/design/components/buttons.html>
|
||||||
class OutlineButton extends MaterialButton {
|
class OutlineButton extends MaterialButton {
|
||||||
/// Create a filled button.
|
/// Create an outline button.
|
||||||
///
|
///
|
||||||
/// The [highlightElevation], [borderWidth], and [clipBehavior]
|
/// The [highlightElevation] argument must be null or a positive value
|
||||||
/// arguments must not be null.
|
/// and the [clipBehavior] argument must not be null.
|
||||||
const OutlineButton({
|
const OutlineButton({
|
||||||
Key key,
|
Key key,
|
||||||
@required VoidCallback onPressed,
|
@required VoidCallback onPressed,
|
||||||
@ -94,8 +97,8 @@ class OutlineButton extends MaterialButton {
|
|||||||
/// The icon and label are arranged in a row and padded by 12 logical pixels
|
/// The icon and label are arranged in a row and padded by 12 logical pixels
|
||||||
/// at the start, and 16 at the end, with an 8 pixel gap in between.
|
/// at the start, and 16 at the end, with an 8 pixel gap in between.
|
||||||
///
|
///
|
||||||
/// The [highlightElevation], [icon], [label], and [clipBehavior] must not be
|
/// The [highlightElevation] argument must be null or a positive value. The
|
||||||
/// null.
|
/// [icon], [label], and [clipBehavior] arguments must not be null.
|
||||||
factory OutlineButton.icon({
|
factory OutlineButton.icon({
|
||||||
Key key,
|
Key key,
|
||||||
@required VoidCallback onPressed,
|
@required VoidCallback onPressed,
|
||||||
@ -118,15 +121,14 @@ class OutlineButton extends MaterialButton {
|
|||||||
|
|
||||||
/// The outline border's color when the button is [enabled] and pressed.
|
/// The outline border's color when the button is [enabled] and pressed.
|
||||||
///
|
///
|
||||||
/// If null this value defaults to the theme's primary color,
|
/// By default the border's color does not change when the button
|
||||||
/// [ThemeData.primaryColor].
|
/// is pressed.
|
||||||
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].
|
||||||
///
|
///
|
||||||
/// If null this value defaults to a very light shade of grey for light
|
/// By default the outline border's color does not change when the
|
||||||
/// themes (see [ThemeData.brightness]), and a very dark shade of grey for
|
/// button is disabled.
|
||||||
/// dark themes.
|
|
||||||
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
|
||||||
@ -136,7 +138,7 @@ class OutlineButton extends MaterialButton {
|
|||||||
/// an outline is not drawn.
|
/// an outline is not drawn.
|
||||||
///
|
///
|
||||||
/// 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 2.0, and its color is a light shade of grey.
|
/// [BorderSide.width] is 1.0, and its color is a light shade of grey.
|
||||||
final BorderSide borderSide;
|
final BorderSide borderSide;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -181,10 +183,10 @@ class OutlineButton extends MaterialButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type of of OutlineButtons created with [OutlineButton.icon].
|
// The type of of OutlineButtons created with OutlineButton.icon.
|
||||||
//
|
//
|
||||||
// This class only exists to give RaisedButtons created with [RaisedButton.icon]
|
// This class only exists to give OutlineButtons created with OutlineButton.icon
|
||||||
// a distinct class for the sake of [ButtonTheme]. It can not be instantiated.
|
// a distinct class for the sake of ButtonTheme. It can not be instantiated.
|
||||||
class _OutlineButtonWithIcon extends OutlineButton with MaterialButtonWithIconMixin {
|
class _OutlineButtonWithIcon extends OutlineButton with MaterialButtonWithIconMixin {
|
||||||
_OutlineButtonWithIcon({
|
_OutlineButtonWithIcon({
|
||||||
Key key,
|
Key key,
|
||||||
@ -291,12 +293,13 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// The Material widget animates its shape (which includes the outline
|
// When highlightElevation > 0.0, the Material widget animates its
|
||||||
// border) and elevation over _kElevationDuration. When pressed, the
|
// shape (which includes the outline border) and elevation over
|
||||||
// button makes its fill color opaque white first, and then sets
|
// _kElevationDuration. When pressed, the button makes its fill
|
||||||
// its highlightElevation. We can't change the elevation while the
|
// color opaque white first, and then sets its
|
||||||
// button's fill is translucent, because the shadow fills the interior
|
// highlightElevation. We can't change the elevation while the
|
||||||
// of the button.
|
// button's fill is translucent, because the shadow fills the
|
||||||
|
// interior of the button.
|
||||||
|
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
duration: _kPressDuration,
|
duration: _kPressDuration,
|
||||||
@ -343,6 +346,8 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
Color _getFillColor() {
|
Color _getFillColor() {
|
||||||
|
if (widget.highlightElevation == null || widget.highlightElevation == 0.0)
|
||||||
|
return Colors.transparent;
|
||||||
final Color color = widget.color ?? Theme.of(context).canvasColor;
|
final Color color = widget.color ?? Theme.of(context).canvasColor;
|
||||||
final Tween<Color> colorTween = ColorTween(
|
final Tween<Color> colorTween = ColorTween(
|
||||||
begin: color.withAlpha(0x00),
|
begin: color.withAlpha(0x00),
|
||||||
@ -352,28 +357,27 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
BorderSide _getOutline() {
|
BorderSide _getOutline() {
|
||||||
final bool isDark = widget.brightness == Brightness.dark;
|
|
||||||
if (widget.borderSide?.style == BorderStyle.none)
|
if (widget.borderSide?.style == BorderStyle.none)
|
||||||
return widget.borderSide;
|
return widget.borderSide;
|
||||||
|
|
||||||
final Color color = widget.enabled
|
final Color specifiedColor = widget.enabled
|
||||||
? (_pressed
|
? (_pressed ? widget.highlightedBorderColor : null) ?? widget.borderSide?.color
|
||||||
? widget.highlightedBorderColor
|
: widget.disabledBorderColor;
|
||||||
: (widget.borderSide?.color ??
|
|
||||||
(isDark ? Colors.grey[600] : Colors.grey[200])))
|
final Color themeColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.12);
|
||||||
: (widget.disabledBorderColor ??
|
|
||||||
(isDark ? Colors.grey[800] : Colors.grey[100]));
|
|
||||||
|
|
||||||
return BorderSide(
|
return BorderSide(
|
||||||
color: color,
|
color: specifiedColor ?? themeColor,
|
||||||
width: widget.borderSide?.width ?? 2.0,
|
width: widget.borderSide?.width ?? 1.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _getHighlightElevation() {
|
double _getHighlightElevation() {
|
||||||
|
if (widget.highlightElevation == null || widget.highlightElevation == 0.0)
|
||||||
|
return 0.0;
|
||||||
return Tween<double>(
|
return Tween<double>(
|
||||||
begin: 0.0,
|
begin: 0.0,
|
||||||
end: widget.highlightElevation ?? 2.0,
|
end: widget.highlightElevation,
|
||||||
).evaluate(_elevationAnimation);
|
).evaluate(_elevationAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,10 @@ void main() {
|
|||||||
shape: const RoundedRectangleBorder(), // default border radius is 0
|
shape: const RoundedRectangleBorder(), // default border radius is 0
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
color: fillColor,
|
color: fillColor,
|
||||||
|
// Causes the button to be filled with the theme's canvasColor
|
||||||
|
// instead of Colors.transparent before the button material's
|
||||||
|
// elevation is animated to 2.0.
|
||||||
|
highlightElevation: 2.0,
|
||||||
highlightedBorderColor: highlightedBorderColor,
|
highlightedBorderColor: highlightedBorderColor,
|
||||||
disabledBorderColor: disabledBorderColor,
|
disabledBorderColor: disabledBorderColor,
|
||||||
borderSide: const BorderSide(
|
borderSide: const BorderSide(
|
||||||
@ -108,7 +112,7 @@ void main() {
|
|||||||
// Expect that the button is disabled and painted with the disabled border color.
|
// Expect that the button is disabled and painted with the disabled border color.
|
||||||
expect(tester.widget<OutlineButton>(outlineButton).enabled, false);
|
expect(tester.widget<OutlineButton>(outlineButton).enabled, false);
|
||||||
expect(
|
expect(
|
||||||
outlineButton, //find.byType(OutlineButton),
|
outlineButton,
|
||||||
paints
|
paints
|
||||||
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
|
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
|
||||||
..path(color: disabledBorderColor, strokeWidth: borderWidth));
|
..path(color: disabledBorderColor, strokeWidth: borderWidth));
|
||||||
@ -323,6 +327,10 @@ void main() {
|
|||||||
body: Center(
|
body: Center(
|
||||||
child: OutlineButton(
|
child: OutlineButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
|
// Causes the button to be filled with the theme's canvasColor
|
||||||
|
// instead of Colors.transparent before the button material's
|
||||||
|
// elevation is animated to 2.0.
|
||||||
|
highlightElevation: 2.0,
|
||||||
child: const Text('Hello'),
|
child: const Text('Hello'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -128,6 +128,37 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('OutlineButton', () {
|
group('OutlineButton', () {
|
||||||
|
testWidgets('theme: ThemeData.light(), enabled: true, highlightElevation: 2.0', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData.light(),
|
||||||
|
home: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
onPressed: () { }, // button.enabled == true
|
||||||
|
// Causes the button to be filled with the theme's canvasColor
|
||||||
|
// instead of Colors.transparent before the button material's
|
||||||
|
// elevation is animated to 2.0.
|
||||||
|
highlightElevation: 2.0,
|
||||||
|
child: const Text('button'),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
|
||||||
|
expect(raw.textStyle.color, const Color(0xdd000000));
|
||||||
|
expect(raw.fillColor, const Color(0x00fafafa));
|
||||||
|
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
|
||||||
|
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
|
||||||
|
expect(raw.elevation, 0.0);
|
||||||
|
expect(raw.highlightElevation, 0.0);
|
||||||
|
expect(raw.disabledElevation, 0.0);
|
||||||
|
expect(raw.constraints, defaultButtonConstraints);
|
||||||
|
expect(raw.padding, defaultButtonPadding);
|
||||||
|
// animationDuration can't be configed by the theme/constructor
|
||||||
|
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('theme: ThemeData.light(), enabled: true', (WidgetTester tester) async {
|
testWidgets('theme: ThemeData.light(), enabled: true', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@ -143,7 +174,7 @@ void main() {
|
|||||||
|
|
||||||
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
|
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
|
||||||
expect(raw.textStyle.color, const Color(0xdd000000));
|
expect(raw.textStyle.color, const Color(0xdd000000));
|
||||||
expect(raw.fillColor, const Color(0x00fafafa));
|
expect(raw.fillColor, Colors.transparent);
|
||||||
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
|
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
|
||||||
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
|
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
|
||||||
expect(raw.elevation, 0.0);
|
expect(raw.elevation, 0.0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user