Skip to content

Commit

Permalink
Added the ability to constrain the size of input decorators (flutter#…
Browse files Browse the repository at this point in the history
  • Loading branch information
darrenaustin authored Apr 21, 2021
1 parent 5526dcc commit 07a6b8f
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 3 deletions.
60 changes: 57 additions & 3 deletions packages/flutter/lib/src/material/input_decorator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2363,7 +2363,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
}

return _Decorator(
final _Decorator decorator = _Decorator(
decoration: _Decoration(
contentPadding: contentPadding,
isCollapsed: decoration!.isCollapsed,
Expand Down Expand Up @@ -2393,6 +2393,15 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
isFocused: isFocused,
expands: widget.expands,
);

final BoxConstraints? constraints = decoration!.constraints ?? themeData.inputDecorationTheme.constraints;
if (constraints != null) {
return ConstrainedBox(
constraints: constraints,
child: decorator,
);
}
return decorator;
}
}

Expand Down Expand Up @@ -2563,6 +2572,7 @@ class InputDecoration {
this.enabled = true,
this.semanticCounterText,
this.alignLabelWithHint,
this.constraints,
}) : assert(enabled != null),
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.');
Expand Down Expand Up @@ -2625,7 +2635,8 @@ class InputDecoration {
disabledBorder = null,
enabledBorder = null,
semanticCounterText = null,
alignLabelWithHint = false;
alignLabelWithHint = false,
constraints = null;

/// An icon to show before the input field and outside of the decoration's
/// container.
Expand Down Expand Up @@ -3320,6 +3331,20 @@ class InputDecoration {
/// Defaults to false.
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
/// by the new values.
InputDecoration copyWith({
Expand Down Expand Up @@ -3367,6 +3392,7 @@ class InputDecoration {
bool? enabled,
String? semanticCounterText,
bool? alignLabelWithHint,
BoxConstraints? constraints,
}) {
return InputDecoration(
icon: icon ?? this.icon,
Expand Down Expand Up @@ -3413,6 +3439,7 @@ class InputDecoration {
enabled: enabled ?? this.enabled,
semanticCounterText: semanticCounterText ?? this.semanticCounterText,
alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint,
constraints: constraints ?? this.constraints,
);
}

Expand Down Expand Up @@ -3448,6 +3475,7 @@ class InputDecoration {
enabledBorder: enabledBorder ?? theme.enabledBorder,
border: border ?? theme.border,
alignLabelWithHint: alignLabelWithHint ?? theme.alignLabelWithHint,
constraints: constraints ?? theme.constraints,
);
}

Expand Down Expand Up @@ -3501,7 +3529,8 @@ class InputDecoration {
&& other.border == border
&& other.enabled == enabled
&& other.semanticCounterText == semanticCounterText
&& other.alignLabelWithHint == alignLabelWithHint;
&& other.alignLabelWithHint == alignLabelWithHint
&& other.constraints == constraints;
}

@override
Expand Down Expand Up @@ -3553,6 +3582,7 @@ class InputDecoration {
enabled,
semanticCounterText,
alignLabelWithHint,
constraints,
];
return hashList(values);
}
Expand Down Expand Up @@ -3600,6 +3630,7 @@ class InputDecoration {
if (!enabled) 'enabled: false',
if (semanticCounterText != null) 'semanticCounterText: $semanticCounterText',
if (alignLabelWithHint != null) 'alignLabelWithHint: $alignLabelWithHint',
if (constraints != null) 'constraints: $constraints',
];
return 'InputDecoration(${description.join(', ')})';
}
Expand Down Expand Up @@ -3651,6 +3682,7 @@ class InputDecorationTheme with Diagnosticable {
this.enabledBorder,
this.border,
this.alignLabelWithHint = false,
this.constraints,
}) : assert(isDense != null),
assert(isCollapsed != null),
assert(filled != null),
Expand Down Expand Up @@ -3972,6 +4004,23 @@ class InputDecorationTheme with Diagnosticable {
/// behavior of aligning the label with the center of the [TextField].
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
/// new values.
InputDecorationTheme copyWith({
Expand Down Expand Up @@ -4004,6 +4053,7 @@ class InputDecorationTheme with Diagnosticable {
InputBorder? enabledBorder,
InputBorder? border,
bool? alignLabelWithHint,
BoxConstraints? constraints,
}) {
return InputDecorationTheme(
labelStyle: labelStyle ?? this.labelStyle,
Expand Down Expand Up @@ -4031,6 +4081,7 @@ class InputDecorationTheme with Diagnosticable {
enabledBorder: enabledBorder ?? this.enabledBorder,
border: border ?? this.border,
alignLabelWithHint: alignLabelWithHint ?? this.alignLabelWithHint,
constraints: constraints ?? this.constraints,
);
}

Expand Down Expand Up @@ -4062,6 +4113,7 @@ class InputDecorationTheme with Diagnosticable {
enabledBorder,
border,
alignLabelWithHint,
constraints,
]);
}

Expand Down Expand Up @@ -4096,6 +4148,7 @@ class InputDecorationTheme with Diagnosticable {
&& other.enabledBorder == enabledBorder
&& other.border == border
&& other.alignLabelWithHint == alignLabelWithHint
&& other.constraints == constraints
&& other.disabledBorder == disabledBorder;
}

Expand Down Expand Up @@ -4128,5 +4181,6 @@ class InputDecorationTheme with Diagnosticable {
properties.add(DiagnosticsProperty<InputBorder>('enabledBorder', enabledBorder, defaultValue: defaultTheme.enabledBorder));
properties.add(DiagnosticsProperty<InputBorder>('border', border, defaultValue: defaultTheme.border));
properties.add(DiagnosticsProperty<bool>('alignLabelWithHint', alignLabelWithHint, defaultValue: defaultTheme.alignLabelWithHint));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: defaultTheme.constraints));
}
}
48 changes: 48 additions & 0 deletions packages/flutter/test/material/input_decorator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,49 @@ void main() {
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('simple case', () {
testWidgets('align top (default)', (WidgetTester tester) async {
Expand Down Expand Up @@ -3139,6 +3182,7 @@ void main() {
focusColor: Colors.blue,
border: InputBorder.none,
alignLabelWithHint: true,
constraints: BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
),
);

Expand All @@ -3155,6 +3199,7 @@ void main() {
expect(decoration.fillColor, Colors.red);
expect(decoration.border, InputBorder.none);
expect(decoration.alignLabelWithHint, true);
expect(decoration.constraints, const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40));

// InputDecoration (baseDecoration) defines InputDecoration properties
decoration = const InputDecoration(
Expand All @@ -3171,6 +3216,7 @@ void main() {
fillColor: Colors.blue,
border: OutlineInputBorder(),
alignLabelWithHint: false,
constraints: BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
).applyDefaults(
const InputDecorationTheme(
labelStyle: themeStyle,
Expand All @@ -3189,6 +3235,7 @@ void main() {
focusColor: Colors.blue,
border: InputBorder.none,
alignLabelWithHint: true,
constraints: BoxConstraints(minWidth: 40, maxWidth: 30, minHeight: 20, maxHeight: 10),
),
);

Expand All @@ -3207,6 +3254,7 @@ void main() {
expect(decoration.fillColor, Colors.blue);
expect(decoration.border, const OutlineInputBorder());
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 {
Expand Down

0 comments on commit 07a6b8f

Please sign in to comment.