Unverified Commit 2d9fad09 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Added ButtonStyle.fixedSize (#74773)

parent 7f4bb1ee
......@@ -107,6 +107,7 @@ class ButtonStyle with Diagnosticable {
this.elevation,
this.padding,
this.minimumSize,
this.fixedSize,
this.side,
this.shape,
this.mouseCursor,
......@@ -158,6 +159,16 @@ class ButtonStyle with Diagnosticable {
/// per [tapTargetSize].
final MaterialStateProperty<Size?>? minimumSize;
/// The button's size.
///
/// This size is still constrained by the style's [minimumSize]. Fixed
/// size dimensions whose value is [double.infinity] are ignored.
///
/// To specify buttons with a fixed width and the default height use
/// `fixedSize: Size.fromWidth(320)`. Similarly, to specify a fixed
/// height and the default width use `fixedSize: Size.fromHeight(100)`.
final MaterialStateProperty<Size?>? fixedSize;
/// The color and weight of the button's outline.
///
/// This value is combined with [shape] to create a shape decorated
......@@ -230,6 +241,7 @@ class ButtonStyle with Diagnosticable {
MaterialStateProperty<double?>? elevation,
MaterialStateProperty<EdgeInsetsGeometry?>? padding,
MaterialStateProperty<Size?>? minimumSize,
MaterialStateProperty<Size?>? fixedSize,
MaterialStateProperty<BorderSide?>? side,
MaterialStateProperty<OutlinedBorder?>? shape,
MaterialStateProperty<MouseCursor?>? mouseCursor,
......@@ -248,6 +260,7 @@ class ButtonStyle with Diagnosticable {
elevation: elevation ?? this.elevation,
padding: padding ?? this.padding,
minimumSize: minimumSize ?? this.minimumSize,
fixedSize: fixedSize ?? this.fixedSize,
side: side ?? this.side,
shape: shape ?? this.shape,
mouseCursor: mouseCursor ?? this.mouseCursor,
......@@ -276,6 +289,7 @@ class ButtonStyle with Diagnosticable {
elevation: elevation ?? style.elevation,
padding: padding ?? style.padding,
minimumSize: minimumSize ?? style.minimumSize,
fixedSize: fixedSize ?? style.fixedSize,
side: side ?? style.side,
shape: shape ?? style.shape,
mouseCursor: mouseCursor ?? style.mouseCursor,
......@@ -298,6 +312,7 @@ class ButtonStyle with Diagnosticable {
elevation,
padding,
minimumSize,
fixedSize,
side,
shape,
mouseCursor,
......@@ -324,6 +339,7 @@ class ButtonStyle with Diagnosticable {
&& other.elevation == elevation
&& other.padding == padding
&& other.minimumSize == minimumSize
&& other.fixedSize == fixedSize
&& other.side == side
&& other.shape == shape
&& other.mouseCursor == mouseCursor
......@@ -345,6 +361,7 @@ class ButtonStyle with Diagnosticable {
properties.add(DiagnosticsProperty<MaterialStateProperty<double?>>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<EdgeInsetsGeometry?>>('padding', padding, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('minimumSize', minimumSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<Size?>>('fixedSize', fixedSize, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<BorderSide?>>('side', side, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<OutlinedBorder?>>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
......@@ -369,6 +386,7 @@ class ButtonStyle with Diagnosticable {
elevation: _lerpProperties<double?>(a?.elevation, b?.elevation, t, lerpDouble),
padding: _lerpProperties<EdgeInsetsGeometry?>(a?.padding, b?.padding, t, EdgeInsetsGeometry.lerp),
minimumSize: _lerpProperties<Size?>(a?.minimumSize, b?.minimumSize, t, Size.lerp),
fixedSize: _lerpProperties<Size?>(a?.fixedSize, b?.fixedSize, t, Size.lerp),
side: _lerpSides(a?.side, b?.side, t),
shape: _lerpShapes(a?.shape, b?.shape, t),
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
......
......@@ -268,6 +268,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
final Color? resolvedShadowColor = resolve<Color?>((ButtonStyle? style) => style?.shadowColor);
final EdgeInsetsGeometry? resolvedPadding = resolve<EdgeInsetsGeometry?>((ButtonStyle? style) => style?.padding);
final Size? resolvedMinimumSize = resolve<Size?>((ButtonStyle? style) => style?.minimumSize);
final Size? resolvedFixedSize = resolve<Size?>((ButtonStyle? style) => style?.fixedSize);
final BorderSide? resolvedSide = resolve<BorderSide?>((ButtonStyle? style) => style?.side);
final OutlinedBorder? resolvedShape = resolve<OutlinedBorder?>((ButtonStyle? style) => style?.shape);
......@@ -285,12 +286,29 @@ class _ButtonStyleState extends State<ButtonStyleButton> with TickerProviderStat
final bool? resolvedEnableFeedback = effectiveValue((ButtonStyle? style) => style?.enableFeedback);
final AlignmentGeometry? resolvedAlignment = effectiveValue((ButtonStyle? style) => style?.alignment);
final Offset densityAdjustment = resolvedVisualDensity!.baseSizeAdjustment;
final BoxConstraints effectiveConstraints = resolvedVisualDensity.effectiveConstraints(
BoxConstraints effectiveConstraints = resolvedVisualDensity.effectiveConstraints(
BoxConstraints(
minWidth: resolvedMinimumSize!.width,
minHeight: resolvedMinimumSize.height,
),
);
if (resolvedFixedSize != null) {
final Size size = effectiveConstraints.constrain(resolvedFixedSize);
if (size.width.isFinite) {
effectiveConstraints = effectiveConstraints.copyWith(
minWidth: size.width,
maxWidth: size.width,
);
}
if (size.height.isFinite) {
effectiveConstraints = effectiveConstraints.copyWith(
minHeight: size.height,
maxHeight: size.height
);
}
}
final EdgeInsetsGeometry padding = resolvedPadding!.add(
EdgeInsets.only(
left: densityAdjustment.dx,
......
......@@ -139,6 +139,7 @@ class ElevatedButton extends ButtonStyleButton {
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
Size? minimumSize,
Size? fixedSize,
BorderSide? side,
OutlinedBorder? shape,
MouseCursor? enabledMouseCursor,
......@@ -174,6 +175,7 @@ class ElevatedButton extends ButtonStyleButton {
elevation: elevationValue,
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
side: ButtonStyleButton.allOrNull<BorderSide>(side),
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
mouseCursor: mouseCursor,
......@@ -231,6 +233,7 @@ class ElevatedButton extends ButtonStyleButton {
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 36)
/// * `fixedSize` - null
/// * `side` - null
/// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
/// * `mouseCursor`
......
......@@ -131,6 +131,7 @@ class OutlinedButton extends ButtonStyleButton {
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
Size? minimumSize,
Size? fixedSize,
BorderSide? side,
OutlinedBorder? shape,
MouseCursor? enabledMouseCursor,
......@@ -160,6 +161,7 @@ class OutlinedButton extends ButtonStyleButton {
elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
side: ButtonStyleButton.allOrNull<BorderSide>(side),
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
mouseCursor: mouseCursor,
......@@ -210,6 +212,7 @@ class OutlinedButton extends ButtonStyleButton {
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 36)
/// * `fixedSize` - null
/// * `side` - BorderSide(width: 1, color: Theme.colorScheme.onSurface(0.12))
/// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
/// * `mouseCursor`
......
......@@ -137,6 +137,7 @@ class TextButton extends ButtonStyleButton {
TextStyle? textStyle,
EdgeInsetsGeometry? padding,
Size? minimumSize,
Size? fixedSize,
BorderSide? side,
OutlinedBorder? shape,
MouseCursor? enabledMouseCursor,
......@@ -166,6 +167,7 @@ class TextButton extends ButtonStyleButton {
elevation: ButtonStyleButton.allOrNull<double>(elevation),
padding: ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(padding),
minimumSize: ButtonStyleButton.allOrNull<Size>(minimumSize),
fixedSize: ButtonStyleButton.allOrNull<Size>(fixedSize),
side: ButtonStyleButton.allOrNull<BorderSide>(side),
shape: ButtonStyleButton.allOrNull<OutlinedBorder>(shape),
mouseCursor: mouseCursor,
......@@ -219,6 +221,7 @@ class TextButton extends ButtonStyleButton {
/// * `2 < textScaleFactor <= 3` - lerp(horizontal(8), horizontal(4))
/// * `3 < textScaleFactor` - horizontal(4)
/// * `minimumSize` - Size(64, 36)
/// * `fixedSize` - null
/// * `side` - null
/// * `shape` - RoundedRectangleBorder(borderRadius: BorderRadius.circular(4))
/// * `mouseCursor`
......
......@@ -24,6 +24,7 @@ void main() {
expect(style.elevation, null);
expect(style.padding, null);
expect(style.minimumSize, null);
expect(style.fixedSize, null);
expect(style.side, null);
expect(style.shape, null);
expect(style.mouseCursor, null);
......@@ -93,6 +94,7 @@ void main() {
final MaterialStateProperty<double> elevation = MaterialStateProperty.all<double>(1);
final MaterialStateProperty<EdgeInsets> padding = MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(1));
final MaterialStateProperty<Size> minimumSize = MaterialStateProperty.all<Size>(const Size(1, 2));
final MaterialStateProperty<Size> fixedSize = MaterialStateProperty.all<Size>(const Size(3, 4));
final MaterialStateProperty<BorderSide> side = MaterialStateProperty.all<BorderSide>(const BorderSide());
final MaterialStateProperty<OutlinedBorder> shape = MaterialStateProperty.all<OutlinedBorder>(const StadiumBorder());
final MaterialStateProperty<MouseCursor> mouseCursor = MaterialStateProperty.all<MouseCursor>(SystemMouseCursors.forbidden);
......@@ -109,6 +111,7 @@ void main() {
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
fixedSize: fixedSize,
side: side,
shape: shape,
mouseCursor: mouseCursor,
......@@ -128,6 +131,7 @@ void main() {
elevation: elevation,
padding: padding,
minimumSize: minimumSize,
fixedSize: fixedSize,
side: side,
shape: shape,
mouseCursor: mouseCursor,
......
......@@ -1039,6 +1039,39 @@ void main() {
expect(find.byType(ElevatedButton), paints ..path(strokeWidth: 4) ..drrect(color: borderColor));
});
testWidgets('Fixed size ElevatedButtons', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(fixedSize: const Size(100, 100)),
onPressed: () {},
child: const Text('100x100'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(fixedSize: const Size.fromWidth(200)),
onPressed: () {},
child: const Text('200xh'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(fixedSize: const Size.fromHeight(200)),
onPressed: () {},
child: const Text('wx200'),
),
],
),
),
),
);
expect(tester.getSize(find.widgetWithText(ElevatedButton, '100x100')), const Size(100, 100));
expect(tester.getSize(find.widgetWithText(ElevatedButton, '200xh')).width, 200);
expect(tester.getSize(find.widgetWithText(ElevatedButton, 'wx200')).height, 200);
});
}
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
......
......@@ -1215,6 +1215,39 @@ void main() {
);
expect(paddingWidget.padding, const EdgeInsets.all(22));
});
testWidgets('Fixed size OutlinedButtons', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
OutlinedButton(
style: OutlinedButton.styleFrom(fixedSize: const Size(100, 100)),
onPressed: () {},
child: const Text('100x100'),
),
OutlinedButton(
style: OutlinedButton.styleFrom(fixedSize: const Size.fromWidth(200)),
onPressed: () {},
child: const Text('200xh'),
),
OutlinedButton(
style: OutlinedButton.styleFrom(fixedSize: const Size.fromHeight(200)),
onPressed: () {},
child: const Text('wx200'),
),
],
),
),
),
);
expect(tester.getSize(find.widgetWithText(OutlinedButton, '100x100')), const Size(100, 100));
expect(tester.getSize(find.widgetWithText(OutlinedButton, '200xh')).width, 200);
expect(tester.getSize(find.widgetWithText(OutlinedButton, 'wx200')).height, 200);
});
}
PhysicalModelLayer _findPhysicalLayer(Element element) {
......
......@@ -1012,8 +1012,39 @@ void main() {
expect(paddingWidget.padding, const EdgeInsets.all(22));
});
}
testWidgets('Fixed size TextButtons', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextButton(
style: TextButton.styleFrom(fixedSize: const Size(100, 100)),
onPressed: () {},
child: const Text('100x100'),
),
TextButton(
style: TextButton.styleFrom(fixedSize: const Size.fromWidth(200)),
onPressed: () {},
child: const Text('200xh'),
),
TextButton(
style: TextButton.styleFrom(fixedSize: const Size.fromHeight(200)),
onPressed: () {},
child: const Text('wx200'),
),
],
),
),
),
);
expect(tester.getSize(find.widgetWithText(TextButton, '100x100')), const Size(100, 100));
expect(tester.getSize(find.widgetWithText(TextButton, '200xh')).width, 200);
expect(tester.getSize(find.widgetWithText(TextButton, 'wx200')).height, 200);
});
}
TextStyle? _iconStyle(WidgetTester tester, IconData icon) {
final RichText iconRichText = tester.widget<RichText>(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment