Unverified Commit 07a6b8f0 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Added the ability to constrain the size of input decorators (#80754)

parent 5526dcc2
...@@ -2363,7 +2363,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat ...@@ -2363,7 +2363,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0)); : const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
} }
return _Decorator( final _Decorator decorator = _Decorator(
decoration: _Decoration( decoration: _Decoration(
contentPadding: contentPadding, contentPadding: contentPadding,
isCollapsed: decoration!.isCollapsed, isCollapsed: decoration!.isCollapsed,
...@@ -2393,6 +2393,15 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat ...@@ -2393,6 +2393,15 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
isFocused: isFocused, isFocused: isFocused,
expands: widget.expands, expands: widget.expands,
); );
final BoxConstraints? constraints = decoration!.constraints ?? themeData.inputDecorationTheme.constraints;
if (constraints != null) {
return ConstrainedBox(
constraints: constraints,
child: decorator,
);
}
return decorator;
} }
} }
...@@ -2563,6 +2572,7 @@ class InputDecoration { ...@@ -2563,6 +2572,7 @@ class InputDecoration {
this.enabled = true, this.enabled = true,
this.semanticCounterText, this.semanticCounterText,
this.alignLabelWithHint, this.alignLabelWithHint,
this.constraints,
}) : assert(enabled != null), }) : assert(enabled != null),
assert(!(prefix != null && prefixText != null), 'Declaring both prefix and prefixText is not supported.'), assert(!(prefix != null && prefixText != null), 'Declaring both prefix and prefixText is not supported.'),
assert(!(suffix != null && suffixText != null), 'Declaring both suffix and suffixText is not supported.'); assert(!(suffix != null && suffixText != null), 'Declaring both suffix and suffixText is not supported.');
...@@ -2625,7 +2635,8 @@ class InputDecoration { ...@@ -2625,7 +2635,8 @@ class InputDecoration {
disabledBorder = null, disabledBorder = null,
enabledBorder = null, enabledBorder = null,
semanticCounterText = null, semanticCounterText = null,
alignLabelWithHint = false; alignLabelWithHint = false,
constraints = null;
/// An icon to show before the input field and outside of the decoration's /// An icon to show before the input field and outside of the decoration's
/// container. /// container.
...@@ -3320,6 +3331,20 @@ class InputDecoration { ...@@ -3320,6 +3331,20 @@ class InputDecoration {
/// Defaults to false. /// Defaults to false.
final bool? alignLabelWithHint; final bool? alignLabelWithHint;
/// Defines minimum and maximum sizes for the [InputDecorator].
///
/// Typically the decorator will fill the horizontal space it is given. For
/// larger screens, it may be useful to have the maximum width clamped to
/// a given value so it doesn't fill the whole screen. This property
/// allows you to control how big the decorator will be in its available
/// space.
///
/// If null, then the ambient [ThemeData.inputDecorationTheme]'s
/// [InputDecorationTheme.constraints] will be used. If that
/// is null then the decorator will fill the available width with
/// a default height based on text size.
final BoxConstraints? constraints;
/// Creates a copy of this input decoration with the given fields replaced /// Creates a copy of this input decoration with the given fields replaced
/// by the new values. /// by the new values.
InputDecoration copyWith({ InputDecoration copyWith({
...@@ -3367,6 +3392,7 @@ class InputDecoration { ...@@ -3367,6 +3392,7 @@ class InputDecoration {
bool? enabled, bool? enabled,
String? semanticCounterText, String? semanticCounterText,
bool? alignLabelWithHint, bool? alignLabelWithHint,
BoxConstraints? constraints,
}) { }) {
return InputDecoration( return InputDecoration(
icon: icon ?? this.icon, icon: icon ?? this.icon,
...@@ -3413,6 +3439,7 @@ class InputDecoration { ...@@ -3413,6 +3439,7 @@ class InputDecoration {
enabled: enabled ?? this.enabled, enabled: enabled ?? this.enabled,
semanticCounterText: semanticCounterText ?? this.semanticCounterText, semanticCounterText: semanticCounterText ?? this.semanticCounterText,
alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint, alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint,
constraints: constraints ?? this.constraints,
); );
} }
...@@ -3448,6 +3475,7 @@ class InputDecoration { ...@@ -3448,6 +3475,7 @@ class InputDecoration {
enabledBorder: enabledBorder ?? theme.enabledBorder, enabledBorder: enabledBorder ?? theme.enabledBorder,
border: border ?? theme.border, border: border ?? theme.border,
alignLabelWithHint: alignLabelWithHint ?? theme.alignLabelWithHint, alignLabelWithHint: alignLabelWithHint ?? theme.alignLabelWithHint,
constraints: constraints ?? theme.constraints,
); );
} }
...@@ -3501,7 +3529,8 @@ class InputDecoration { ...@@ -3501,7 +3529,8 @@ class InputDecoration {
&& other.border == border && other.border == border
&& other.enabled == enabled && other.enabled == enabled
&& other.semanticCounterText == semanticCounterText && other.semanticCounterText == semanticCounterText
&& other.alignLabelWithHint == alignLabelWithHint; && other.alignLabelWithHint == alignLabelWithHint
&& other.constraints == constraints;
} }
@override @override
...@@ -3553,6 +3582,7 @@ class InputDecoration { ...@@ -3553,6 +3582,7 @@ class InputDecoration {
enabled, enabled,
semanticCounterText, semanticCounterText,
alignLabelWithHint, alignLabelWithHint,
constraints,
]; ];
return hashList(values); return hashList(values);
} }
...@@ -3600,6 +3630,7 @@ class InputDecoration { ...@@ -3600,6 +3630,7 @@ class InputDecoration {
if (!enabled) 'enabled: false', if (!enabled) 'enabled: false',
if (semanticCounterText != null) 'semanticCounterText: $semanticCounterText', if (semanticCounterText != null) 'semanticCounterText: $semanticCounterText',
if (alignLabelWithHint != null) 'alignLabelWithHint: $alignLabelWithHint', if (alignLabelWithHint != null) 'alignLabelWithHint: $alignLabelWithHint',
if (constraints != null) 'constraints: $constraints',
]; ];
return 'InputDecoration(${description.join(', ')})'; return 'InputDecoration(${description.join(', ')})';
} }
...@@ -3651,6 +3682,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3651,6 +3682,7 @@ class InputDecorationTheme with Diagnosticable {
this.enabledBorder, this.enabledBorder,
this.border, this.border,
this.alignLabelWithHint = false, this.alignLabelWithHint = false,
this.constraints,
}) : assert(isDense != null), }) : assert(isDense != null),
assert(isCollapsed != null), assert(isCollapsed != null),
assert(filled != null), assert(filled != null),
...@@ -3972,6 +4004,23 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3972,6 +4004,23 @@ class InputDecorationTheme with Diagnosticable {
/// behavior of aligning the label with the center of the [TextField]. /// behavior of aligning the label with the center of the [TextField].
final bool alignLabelWithHint; final bool alignLabelWithHint;
/// Defines minimum and maximum sizes for the [InputDecorator].
///
/// Typically the decorator will fill the horizontal space it is given. For
/// larger screens, it may be useful to have the maximum width clamped to
/// a given value so it doesn't fill the whole screen. This property
/// allows you to control how big the decorator will be in its available
/// space.
///
/// If null, then the decorator will fill the available width with
/// a default height based on text size.
///
/// See also:
///
/// * [InputDecoration.constraints], which can override this setting for a
/// given decorator.
final BoxConstraints? constraints;
/// Creates a copy of this object but with the given fields replaced with the /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
InputDecorationTheme copyWith({ InputDecorationTheme copyWith({
...@@ -4004,6 +4053,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4004,6 +4053,7 @@ class InputDecorationTheme with Diagnosticable {
InputBorder? enabledBorder, InputBorder? enabledBorder,
InputBorder? border, InputBorder? border,
bool? alignLabelWithHint, bool? alignLabelWithHint,
BoxConstraints? constraints,
}) { }) {
return InputDecorationTheme( return InputDecorationTheme(
labelStyle: labelStyle ?? this.labelStyle, labelStyle: labelStyle ?? this.labelStyle,
...@@ -4031,6 +4081,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4031,6 +4081,7 @@ class InputDecorationTheme with Diagnosticable {
enabledBorder: enabledBorder ?? this.enabledBorder, enabledBorder: enabledBorder ?? this.enabledBorder,
border: border ?? this.border, border: border ?? this.border,
alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint, alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint,
constraints: constraints ?? this.constraints,
); );
} }
...@@ -4062,6 +4113,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4062,6 +4113,7 @@ class InputDecorationTheme with Diagnosticable {
enabledBorder, enabledBorder,
border, border,
alignLabelWithHint, alignLabelWithHint,
constraints,
]); ]);
} }
...@@ -4096,6 +4148,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4096,6 +4148,7 @@ class InputDecorationTheme with Diagnosticable {
&& other.enabledBorder == enabledBorder && other.enabledBorder == enabledBorder
&& other.border == border && other.border == border
&& other.alignLabelWithHint == alignLabelWithHint && other.alignLabelWithHint == alignLabelWithHint
&& other.constraints == constraints
&& other.disabledBorder == disabledBorder; && other.disabledBorder == disabledBorder;
} }
...@@ -4128,5 +4181,6 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4128,5 +4181,6 @@ class InputDecorationTheme with Diagnosticable {
properties.add(DiagnosticsProperty<InputBorder>('enabledBorder', enabledBorder, defaultValue: defaultTheme.enabledBorder)); properties.add(DiagnosticsProperty<InputBorder>('enabledBorder', enabledBorder, defaultValue: defaultTheme.enabledBorder));
properties.add(DiagnosticsProperty<InputBorder>('border', border, defaultValue: defaultTheme.border)); properties.add(DiagnosticsProperty<InputBorder>('border', border, defaultValue: defaultTheme.border));
properties.add(DiagnosticsProperty<bool>('alignLabelWithHint', alignLabelWithHint, defaultValue: defaultTheme.alignLabelWithHint)); properties.add(DiagnosticsProperty<bool>('alignLabelWithHint', alignLabelWithHint, defaultValue: defaultTheme.alignLabelWithHint));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: defaultTheme.constraints));
} }
} }
...@@ -1728,6 +1728,49 @@ void main() { ...@@ -1728,6 +1728,49 @@ void main() {
expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 0.0); expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 0.0);
}); });
group('constraints', () {
testWidgets('No InputDecorator constraints', (WidgetTester tester) async {
await tester.pumpWidget(buildInputDecorator());
// Should fill the screen width and be default height
expect(tester.getSize(find.byType(InputDecorator)), const Size(800, 48));
});
testWidgets('InputDecoratorThemeData constraints', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
theme: ThemeData(
inputDecorationTheme: const InputDecorationTheme(
constraints: BoxConstraints(maxWidth: 300, maxHeight: 40),
),
),
)
);
// Theme settings should make it 300x40 pixels
expect(tester.getSize(find.byType(InputDecorator)), const Size(300, 40));
});
testWidgets('InputDecorator constraints', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
theme: ThemeData(
inputDecorationTheme: const InputDecorationTheme(
constraints: BoxConstraints(maxWidth: 300, maxHeight: 40),
),
),
decoration: const InputDecoration(
constraints: BoxConstraints(maxWidth: 200, maxHeight: 32),
),
)
);
// InputDecoration.constraints should override the theme. It should be
// only 200x32 pixels
expect(tester.getSize(find.byType(InputDecorator)), const Size(200, 32));
});
});
group('textAlignVertical position', () { group('textAlignVertical position', () {
group('simple case', () { group('simple case', () {
testWidgets('align top (default)', (WidgetTester tester) async { testWidgets('align top (default)', (WidgetTester tester) async {
...@@ -3139,6 +3182,7 @@ void main() { ...@@ -3139,6 +3182,7 @@ void main() {
focusColor: Colors.blue, focusColor: Colors.blue,
border: InputBorder.none, border: InputBorder.none,
alignLabelWithHint: true, alignLabelWithHint: true,
constraints: BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
), ),
); );
...@@ -3155,6 +3199,7 @@ void main() { ...@@ -3155,6 +3199,7 @@ void main() {
expect(decoration.fillColor, Colors.red); expect(decoration.fillColor, Colors.red);
expect(decoration.border, InputBorder.none); expect(decoration.border, InputBorder.none);
expect(decoration.alignLabelWithHint, true); expect(decoration.alignLabelWithHint, true);
expect(decoration.constraints, const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40));
// InputDecoration (baseDecoration) defines InputDecoration properties // InputDecoration (baseDecoration) defines InputDecoration properties
decoration = const InputDecoration( decoration = const InputDecoration(
...@@ -3171,6 +3216,7 @@ void main() { ...@@ -3171,6 +3216,7 @@ void main() {
fillColor: Colors.blue, fillColor: Colors.blue,
border: OutlineInputBorder(), border: OutlineInputBorder(),
alignLabelWithHint: false, alignLabelWithHint: false,
constraints: BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
).applyDefaults( ).applyDefaults(
const InputDecorationTheme( const InputDecorationTheme(
labelStyle: themeStyle, labelStyle: themeStyle,
...@@ -3189,6 +3235,7 @@ void main() { ...@@ -3189,6 +3235,7 @@ void main() {
focusColor: Colors.blue, focusColor: Colors.blue,
border: InputBorder.none, border: InputBorder.none,
alignLabelWithHint: true, alignLabelWithHint: true,
constraints: BoxConstraints(minWidth: 40, maxWidth: 30, minHeight: 20, maxHeight: 10),
), ),
); );
...@@ -3207,6 +3254,7 @@ void main() { ...@@ -3207,6 +3254,7 @@ void main() {
expect(decoration.fillColor, Colors.blue); expect(decoration.fillColor, Colors.blue);
expect(decoration.border, const OutlineInputBorder()); expect(decoration.border, const OutlineInputBorder());
expect(decoration.alignLabelWithHint, false); expect(decoration.alignLabelWithHint, false);
expect(decoration.constraints, const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40));
}); });
testWidgets('InputDecorator OutlineInputBorder fillColor is clipped by border', (WidgetTester tester) async { testWidgets('InputDecorator OutlineInputBorder fillColor is clipped by border', (WidgetTester tester) async {
......
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