Unverified Commit 64b0cfda authored by Bjarte Bore's avatar Bjarte Bore Committed by GitHub

Added support for MaterialState to InputDecorator (#91762)

parent 06e75e94
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for InputDecoration
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
initialValue: 'abc',
decoration: InputDecoration(
prefixIcon: const Icon(Icons.person),
prefixIconColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.focused)) {
return Colors.green;
} if (states.contains(MaterialState.error)) {
return Colors.red;
}
return Colors.grey;
}),
),
);
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for InputDecoration
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
);
}
}
class MyStatelessWidget extends StatelessWidget {
const MyStatelessWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
return Theme(
data: themeData.copyWith(
inputDecorationTheme: themeData.inputDecorationTheme.copyWith(
prefixIconColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.focused)) {
return Colors.green;
} if (states.contains(MaterialState.error)) {
return Colors.red;
}
return Colors.grey;
}),
)
),
child: TextFormField(
initialValue: 'abc',
decoration: const InputDecoration(
prefixIcon: Icon(Icons.person),
),
),
);
}
}
......@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'constants.dart';
import 'input_border.dart';
import 'material_state.dart';
import 'theme.dart';
import 'theme_data.dart';
......@@ -2081,7 +2082,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
if (decoration!.filled != true) // filled == null same as filled == false
return Colors.transparent;
if (decoration!.fillColor != null)
return decoration!.fillColor!;
return MaterialStateProperty.resolveAs(decoration!.fillColor!, materialState);
// dark theme: 10% white (enabled), 5% white (disabled)
// light theme: 4% black (enabled), 2% black (disabled)
......@@ -2104,16 +2105,33 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
return decoration!.hoverColor ?? themeData.inputDecorationTheme.hoverColor ?? themeData.hoverColor;
}
Color _getDefaultIconColor(ThemeData themeData) {
if (!decoration!.enabled && !isFocused)
return themeData.disabledColor;
Color _getIconColor(ThemeData themeData) {
Color _resolveIconColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled) && !states.contains(MaterialState.focused))
return themeData.disabledColor;
switch (themeData.brightness) {
case Brightness.dark:
return Colors.white70;
case Brightness.light:
return Colors.black45;
if (states.contains(MaterialState.focused))
return themeData.colorScheme.primary;
switch (themeData.brightness) {
case Brightness.dark:
return Colors.white70;
case Brightness.light:
return Colors.black45;
}
}
return MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.iconColor, materialState)
?? MaterialStateProperty.resolveWith(_resolveIconColor).resolve(materialState);
}
Color _getPrefixIconColor(ThemeData themeData) {
return MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.prefixIconColor, materialState)
?? _getIconColor(themeData);
}
Color _getSuffixIconColor(ThemeData themeData) {
return MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.suffixIconColor, materialState)
?? _getIconColor(themeData);
}
// True if the label will be shown and the hint will not.
......@@ -2129,34 +2147,68 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
// If the label is a floating placeholder, it's always shown.
bool get _shouldShowLabel => _hasInlineLabel || _floatingLabelEnabled;
// The base style for the inline label or hint when they're displayed "inline",
// The base style for the inline label when they're displayed "inline",
// i.e. when they appear in place of the empty text field.
TextStyle _getInlineStyle(ThemeData themeData) {
return themeData.textTheme.subtitle1!.merge(widget.baseStyle)
.copyWith(color: decoration!.enabled ? themeData.hintColor : themeData.disabledColor);
TextStyle _getInlineLabelStyle(ThemeData themeData) {
final TextStyle defaultStyle = TextStyle(
color: decoration!.enabled ? themeData.hintColor : themeData.disabledColor,
);
final TextStyle? style = MaterialStateProperty.resolveAs(decoration!.labelStyle, materialState)
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.labelStyle, materialState);
return themeData.textTheme.subtitle1!
.merge(widget.baseStyle)
.merge(defaultStyle)
.merge(style)
// Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
// Setting TextStyle.height to 1 ensures that the label's height will equal
// its font size.
.copyWith(height: themeData.fixTextFieldOutlineLabel ? 1 : null);
}
// The base style for the inline hint when they're displayed "inline",
// i.e. when they appear in place of the empty text field.
TextStyle _getInlineHintStyle(ThemeData themeData) {
final TextStyle defaultStyle = TextStyle(
color: decoration!.enabled ? themeData.hintColor : themeData.disabledColor,
);
final TextStyle? style = MaterialStateProperty.resolveAs(decoration!.hintStyle, materialState)
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.hintStyle, materialState);
return themeData.textTheme.subtitle1!
.merge(widget.baseStyle)
.merge(defaultStyle)
.merge(style);
}
TextStyle _getFloatingLabelStyle(ThemeData themeData) {
final Color color = decoration!.errorText != null
? decoration!.errorStyle?.color ?? themeData.errorColor
: _getActiveColor(themeData);
final TextStyle style = themeData.textTheme.subtitle1!.merge(widget.baseStyle);
// Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
// Setting TextStyle.height to 1 ensures that the label's height will equal
// its font size.
return themeData.fixTextFieldOutlineLabel
? style
.copyWith(height: 1, color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle)
: style
.copyWith(color: decoration!.enabled ? color : themeData.disabledColor)
TextStyle getFallbackTextStyle() {
final Color color = decoration!.errorText != null
? decoration!.errorStyle?.color ?? themeData.errorColor
: _getActiveColor(themeData);
return TextStyle(color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle);
}
final TextStyle? style = MaterialStateProperty.resolveAs(decoration!.floatingLabelStyle, materialState)
?? MaterialStateProperty.resolveAs(themeData.inputDecorationTheme.floatingLabelStyle, materialState);
return themeData.textTheme.subtitle1!
.merge(widget.baseStyle)
// Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
// Setting TextStyle.height to 1 ensures that the label's height will equal
// its font size.
.copyWith(height: themeData.fixTextFieldOutlineLabel ? 1 : null)
.merge(getFallbackTextStyle())
.merge(style);
}
TextStyle _getHelperStyle(ThemeData themeData) {
final Color color = decoration!.enabled ? themeData.hintColor : Colors.transparent;
return themeData.textTheme.caption!.copyWith(color: color).merge(decoration!.helperStyle);
return themeData.textTheme.caption!.copyWith(color: color).merge(MaterialStateProperty.resolveAs(decoration!.helperStyle, materialState));
}
TextStyle _getErrorStyle(ThemeData themeData) {
......@@ -2164,9 +2216,25 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
return themeData.textTheme.caption!.copyWith(color: color).merge(decoration!.errorStyle);
}
Set<MaterialState> get materialState {
return <MaterialState>{
if (!decoration!.enabled) MaterialState.disabled,
if (isFocused) MaterialState.focused,
if (isHovering) MaterialState.hovered,
if (decoration!.errorText != null) MaterialState.error,
};
}
InputBorder _getDefaultBorder(ThemeData themeData) {
if (decoration!.border?.borderSide == BorderSide.none) {
return decoration!.border!;
final InputBorder border = MaterialStateProperty.resolveAs(decoration!.border, materialState)
?? const UnderlineInputBorder();
if (decoration!.border is MaterialStateProperty<InputBorder>) {
return border;
}
if (border.borderSide == BorderSide.none) {
return border;
}
final Color borderColor;
......@@ -2186,17 +2254,16 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
else
borderWeight = isFocused ? 2.0 : 1.0;
final InputBorder border = decoration!.border ?? const UnderlineInputBorder();
return border.copyWith(borderSide: BorderSide(color: borderColor, width: borderWeight));
}
@override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final TextStyle inlineStyle = _getInlineStyle(themeData);
final TextBaseline textBaseline = inlineStyle.textBaseline!;
final TextStyle labelStyle = _getInlineLabelStyle(themeData);
final TextBaseline textBaseline = labelStyle.textBaseline!;
final TextStyle hintStyle = inlineStyle.merge(decoration!.hintStyle);
final TextStyle hintStyle = _getInlineHintStyle(themeData);
final Widget? hint = decoration!.hintText == null ? null : AnimatedOpacity(
opacity: (isEmpty && !_hasInlineLabel) ? 1.0 : 0.0,
duration: _kTransitionDuration,
......@@ -2231,12 +2298,6 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
isHovering: isHovering,
);
// Temporary opt-in fix for https://github.com/flutter/flutter/issues/54028
// Setting TextStyle.height to 1 ensures that the label's height will equal
// its font size.
final TextStyle inlineLabelStyle = themeData.fixTextFieldOutlineLabel
? inlineStyle.merge(decoration!.labelStyle).copyWith(height: 1)
: inlineStyle.merge(decoration!.labelStyle);
final Widget? label = decoration!.labelText == null && decoration!.label == null ? null : _Shaker(
animation: _shakingLabelController.view,
child: AnimatedOpacity(
......@@ -2248,7 +2309,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
curve: _kTransitionCurve,
style: widget._labelShouldWithdraw
? _getFloatingLabelStyle(themeData)
: inlineLabelStyle,
: labelStyle,
child: decoration!.label ?? Text(
decoration!.labelText!,
overflow: TextOverflow.ellipsis,
......@@ -2262,7 +2323,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
_AffixText(
labelIsFloating: widget._labelShouldWithdraw,
text: decoration!.prefixText,
style: decoration!.prefixStyle ?? hintStyle,
style: MaterialStateProperty.resolveAs(decoration!.prefixStyle, materialState) ?? hintStyle,
child: decoration!.prefix,
);
......@@ -2270,21 +2331,20 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
_AffixText(
labelIsFloating: widget._labelShouldWithdraw,
text: decoration!.suffixText,
style: decoration!.suffixStyle ?? hintStyle,
style: MaterialStateProperty.resolveAs(decoration!.suffixStyle, materialState) ?? hintStyle,
child: decoration!.suffix,
);
final Color activeColor = _getActiveColor(themeData);
final bool decorationIsDense = decoration!.isDense == true; // isDense == null, same as false
final double iconSize = decorationIsDense ? 18.0 : 24.0;
final Color iconColor = isFocused ? activeColor : _getDefaultIconColor(themeData);
final Widget? icon = decoration!.icon == null ? null :
Padding(
padding: const EdgeInsetsDirectional.only(end: 16.0),
child: IconTheme.merge(
data: IconThemeData(
color: iconColor,
color: _getIconColor(themeData),
size: iconSize,
),
child: decoration!.icon!,
......@@ -2304,7 +2364,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
),
child: IconTheme.merge(
data: IconThemeData(
color: iconColor,
color: _getPrefixIconColor(themeData),
size: iconSize,
),
child: decoration!.prefixIcon!,
......@@ -2325,7 +2385,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
),
child: IconTheme.merge(
data: IconThemeData(
color: iconColor,
color: _getSuffixIconColor(themeData),
size: iconSize,
),
child: decoration!.suffixIcon!,
......@@ -2352,7 +2412,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
liveRegion: isFocused,
child: Text(
decoration!.counterText!,
style: _getHelperStyle(themeData).merge(decoration!.counterStyle),
style: _getHelperStyle(themeData).merge(MaterialStateProperty.resolveAs(decoration!.counterStyle, materialState)),
overflow: TextOverflow.ellipsis,
semanticsLabel: decoration!.semanticCounterText,
),
......@@ -2371,7 +2431,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
contentPadding = decorationContentPadding ?? EdgeInsets.zero;
} else if (!border.isOutline) {
// 4.0: the vertical gap between the inline elements and the floating label.
floatingLabelHeight = (4.0 + 0.75 * inlineLabelStyle.fontSize!) * MediaQuery.textScaleFactorOf(context);
floatingLabelHeight = (4.0 + 0.75 * labelStyle.fontSize!) * MediaQuery.textScaleFactorOf(context);
if (decoration!.filled == true) { // filled == null same as filled == false
contentPadding = decorationContentPadding ?? (decorationIsDense
? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
......@@ -2482,6 +2542,22 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
/// ** See code in examples/api/lib/material/input_decorator/input_decoration.3.dart **
/// {@end-tool}
///
/// {@tool dartpad --template=stateless_widget_scaffold}
/// This sample shows how to style a `TextField` with a prefixIcon that changes color
/// based on the `MaterialState`. The color defaults to gray, be blue while focused
/// and red if in an error state.
///
/// ** See code in examples/api/lib/material/input_decorator/input_decoration.material_state.0.dart **
/// {@end-tool}
///
/// {@tool dartpad --template=stateless_widget_scaffold}
/// This sample shows how to style a `TextField` with a prefixIcon that changes color
/// based on the `MaterialState` through the use of `ThemeData`. The color defaults
/// to gray, be blue while focused and red if in an error state.
///
/// ** See code in examples/api/lib/material/input_decorator/input_decoration.material_state.1.dart **
/// {@end-tool}
///
/// See also:
///
/// * [TextField], which is a text input widget that uses an
......@@ -2507,6 +2583,7 @@ class InputDecoration {
/// Similarly, only one of [suffix] and [suffixText] can be specified.
const InputDecoration({
this.icon,
this.iconColor,
this.label,
this.labelText,
this.labelStyle,
......@@ -2530,10 +2607,12 @@ class InputDecoration {
this.prefix,
this.prefixText,
this.prefixStyle,
this.prefixIconColor,
this.suffixIcon,
this.suffix,
this.suffixText,
this.suffixStyle,
this.suffixIconColor,
this.suffixIconConstraints,
this.counter,
this.counterText,
......@@ -2575,6 +2654,7 @@ class InputDecoration {
this.enabled = true,
}) : assert(enabled != null),
icon = null,
iconColor = null,
label = null,
labelText = null,
labelStyle = null,
......@@ -2593,11 +2673,13 @@ class InputDecoration {
prefix = null,
prefixText = null,
prefixStyle = null,
prefixIconColor = null,
prefixIconConstraints = null,
suffix = null,
suffixIcon = null,
suffixText = null,
suffixStyle = null,
suffixIconColor = null,
suffixIconConstraints = null,
counter = null,
counterText = null,
......@@ -2628,6 +2710,13 @@ class InputDecoration {
/// See [Icon], [ImageIcon].
final Widget? icon;
/// The color of the [icon].
///
/// If [iconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
final Color? iconColor;
/// Optional widget that describes the input field.
///
/// {@template flutter.material.inputDecoration.label}
......@@ -2662,6 +2751,10 @@ class InputDecoration {
/// The style to use for the [labelText] when the label is on top of the
/// input field.
///
/// If [labelStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// When the [labelText] is above (i.e., vertically adjacent to) the input
/// field, the text uses the [floatingLabelStyle] instead.
///
......@@ -2672,6 +2765,10 @@ class InputDecoration {
/// The style to use for the [labelText] when the label is above (i.e.,
/// vertically adjacent to) the input field.
///
/// If [floatingLabelStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to [labelStyle].
final TextStyle? floatingLabelStyle;
......@@ -2684,6 +2781,10 @@ class InputDecoration {
final String? helperText;
/// The style to use for the [helperText].
///
/// If [helperStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
final TextStyle? helperStyle;
/// The maximum number of lines the [helperText] can occupy.
......@@ -2709,6 +2810,10 @@ class InputDecoration {
/// The style to use for the [hintText].
///
/// If [hintStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// Also used for the [labelText] when the [labelText] is displayed on
/// top of the input field (i.e., at the same location on the screen where
/// text may be entered in the [InputDecorator.child]).
......@@ -2904,6 +3009,10 @@ class InputDecoration {
/// The style to use for the [prefixText].
///
/// If [prefixStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [hintStyle].
///
/// See also:
......@@ -2911,6 +3020,15 @@ class InputDecoration {
/// * [suffixStyle], the equivalent but on the trailing edge.
final TextStyle? prefixStyle;
/// Optional color of the prefixIcon
///
/// Defaults to [iconColor]
///
/// If [prefixIconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
final Color? prefixIconColor;
/// An icon that appears after the editable part of the text field and
/// after the [suffix] or [suffixText], within the decoration's container.
///
......@@ -2977,6 +3095,10 @@ class InputDecoration {
/// The style to use for the [suffixText].
///
/// If [suffixStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [hintStyle].
///
/// See also:
......@@ -2984,6 +3106,15 @@ class InputDecoration {
/// * [prefixStyle], the equivalent but on the leading edge.
final TextStyle? suffixStyle;
/// Optional color of the suffixIcon
///
/// Defaults to [iconColor]
///
/// If [suffixIconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
final Color? suffixIconColor;
/// The constraints for the suffix icon.
///
/// This can be used to modify the [BoxConstraints] surrounding [suffixIcon].
......@@ -3028,6 +3159,10 @@ class InputDecoration {
/// The style to use for the [counterText].
///
/// If [counterStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [helperStyle].
final TextStyle? counterStyle;
......@@ -3204,11 +3339,15 @@ class InputDecoration {
/// The shape of the border to draw around the decoration's container.
///
/// This border's [InputBorder.borderSide], i.e. the border's color and width,
/// will be overridden to reflect the input decorator's state. Only the
/// border's shape is used. If custom [BorderSide] values are desired for
/// a given state, all four borders – [errorBorder], [focusedBorder],
/// [enabledBorder], [disabledBorder] – must be set.
/// If [border] is a [MaterialStateUnderlineInputBorder]
/// or [MaterialStateOutlineInputBorder], then the effective border can depend on
/// the [MaterialState.focused] state, i.e. if the [TextField] is focused or not.
///
/// If [border] derives from [InputBorder] the border's [InputBorder.borderSide],
/// i.e. the border's color and width, will be overridden to reflect the input
/// decorator's state. Only the border's shape is used. If custom [BorderSide]
/// values are desired for a given state, all four borders – [errorBorder],
/// [focusedBorder], [enabledBorder], [disabledBorder] – must be set.
///
/// The decoration's container is the area which is filled if [filled] is
/// true and bordered per the [border]. It's the area adjacent to
......@@ -3276,6 +3415,7 @@ class InputDecoration {
/// by the new values.
InputDecoration copyWith({
Widget? icon,
Color? iconColor,
Widget? label,
String? labelText,
TextStyle? labelStyle,
......@@ -3299,10 +3439,12 @@ class InputDecoration {
String? prefixText,
BoxConstraints? prefixIconConstraints,
TextStyle? prefixStyle,
Color? prefixIconColor,
Widget? suffixIcon,
Widget? suffix,
String? suffixText,
TextStyle? suffixStyle,
Color? suffixIconColor,
BoxConstraints? suffixIconConstraints,
Widget? counter,
String? counterText,
......@@ -3324,6 +3466,7 @@ class InputDecoration {
}) {
return InputDecoration(
icon: icon ?? this.icon,
iconColor: iconColor ?? this.iconColor,
label: label ?? this.label,
labelText: labelText ?? this.labelText,
labelStyle: labelStyle ?? this.labelStyle,
......@@ -3346,11 +3489,13 @@ class InputDecoration {
prefix: prefix ?? this.prefix,
prefixText: prefixText ?? this.prefixText,
prefixStyle: prefixStyle ?? this.prefixStyle,
prefixIconColor: prefixIconColor ?? this.prefixIconColor,
prefixIconConstraints: prefixIconConstraints ?? this.prefixIconConstraints,
suffixIcon: suffixIcon ?? this.suffixIcon,
suffix: suffix ?? this.suffix,
suffixText: suffixText ?? this.suffixText,
suffixStyle: suffixStyle ?? this.suffixStyle,
suffixIconColor: suffixIconColor ?? this.suffixIconColor,
suffixIconConstraints: suffixIconConstraints ?? this.suffixIconConstraints,
counter: counter ?? this.counter,
counterText: counterText ?? this.counterText,
......@@ -3416,6 +3561,7 @@ class InputDecoration {
return false;
return other is InputDecoration
&& other.icon == icon
&& other.iconColor == iconColor
&& other.label == label
&& other.labelText == labelText
&& other.labelStyle == labelStyle
......@@ -3435,11 +3581,13 @@ class InputDecoration {
&& other.contentPadding == contentPadding
&& other.isCollapsed == isCollapsed
&& other.prefixIcon == prefixIcon
&& other.prefixIconColor == prefixIconColor
&& other.prefix == prefix
&& other.prefixText == prefixText
&& other.prefixStyle == prefixStyle
&& other.prefixIconConstraints == prefixIconConstraints
&& other.suffixIcon == suffixIcon
&& other.suffixIconColor == suffixIconColor
&& other.suffix == suffix
&& other.suffixText == suffixText
&& other.suffixStyle == suffixStyle
......@@ -3467,6 +3615,7 @@ class InputDecoration {
int get hashCode {
final List<Object?> values = <Object?>[
icon,
iconColor,
label,
labelText,
floatingLabelStyle,
......@@ -3492,11 +3641,13 @@ class InputDecoration {
border,
enabled,
prefixIcon,
prefixIconColor,
prefix,
prefixText,
prefixStyle,
prefixIconConstraints,
suffixIcon,
suffixIconColor,
suffix,
suffixText,
suffixStyle,
......@@ -3522,6 +3673,7 @@ class InputDecoration {
String toString() {
final List<String> description = <String>[
if (icon != null) 'icon: $icon',
if (iconColor != null) 'iconColor: $iconColor',
if (label != null) 'label: $label',
if (labelText != null) 'labelText: "$labelText"',
if (floatingLabelStyle != null) 'floatingLabelStyle: "$floatingLabelStyle"',
......@@ -3537,11 +3689,13 @@ class InputDecoration {
if (contentPadding != null) 'contentPadding: $contentPadding',
if (isCollapsed) 'isCollapsed: $isCollapsed',
if (prefixIcon != null) 'prefixIcon: $prefixIcon',
if (prefixIconColor != null) 'prefixIconColor: $prefixIconColor',
if (prefix != null) 'prefix: $prefix',
if (prefixText != null) 'prefixText: $prefixText',
if (prefixStyle != null) 'prefixStyle: $prefixStyle',
if (prefixIconConstraints != null) 'prefixIconConstraints: $prefixIconConstraints',
if (suffixIcon != null) 'suffixIcon: $suffixIcon',
if (suffixIconColor != null) 'suffixIconColor: $suffixIconColor',
if (suffix != null) 'suffix: $suffix',
if (suffixText != null) 'suffixText: $suffixText',
if (suffixStyle != null) 'suffixStyle: $suffixStyle',
......@@ -3596,8 +3750,11 @@ class InputDecorationTheme with Diagnosticable {
this.isDense = false,
this.contentPadding,
this.isCollapsed = false,
this.iconColor,
this.prefixStyle,
this.prefixIconColor,
this.suffixStyle,
this.suffixIconColor,
this.counterStyle,
this.filled = false,
this.fillColor,
......@@ -3622,6 +3779,10 @@ class InputDecorationTheme with Diagnosticable {
/// When the [InputDecoration.labelText] is floating above the input field,
/// the text uses the [floatingLabelStyle] instead.
///
/// If [labelStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
final TextStyle? labelStyle;
......@@ -3632,10 +3793,18 @@ class InputDecorationTheme with Diagnosticable {
/// When the [InputDecoration.labelText] is on top of the input field, the
/// text uses the [labelStyle] instead.
///
/// If [floatingLabelStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to [labelStyle].
final TextStyle? floatingLabelStyle;
/// The style to use for [InputDecoration.helperText].
///
/// If [helperStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
final TextStyle? helperStyle;
/// The maximum number of lines the [InputDecoration.helperText] can occupy.
......@@ -3653,6 +3822,10 @@ class InputDecorationTheme with Diagnosticable {
/// The style to use for the [InputDecoration.hintText].
///
/// If [hintStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// Also used for the [InputDecoration.labelText] when the
/// [InputDecoration.labelText] is displayed on top of the input field (i.e.,
/// at the same location on the screen where text may be entered in the input
......@@ -3712,18 +3885,57 @@ class InputDecorationTheme with Diagnosticable {
/// [InputDecoration.errorText], or an [InputDecoration.icon].
final bool isCollapsed;
/// The Color to use for the [InputDecoration.icon].
///
/// If [iconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [ColorScheme.primary].
final Color? iconColor;
/// The style to use for the [InputDecoration.prefixText].
///
/// If [prefixStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [hintStyle].
final TextStyle? prefixStyle;
/// The Color to use for the [InputDecoration.prefixIcon].
///
/// If [prefixIconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [ColorScheme.primary].
final Color? prefixIconColor;
/// The style to use for the [InputDecoration.suffixText].
///
/// If [suffixStyle] is a [MaterialStateTextStyle], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [hintStyle].
final TextStyle? suffixStyle;
/// The Color to use for the [InputDecoration.suffixIcon].
///
/// If [suffixIconColor] is a [MaterialStateColor], then the effective
/// color can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [ColorScheme.primary].
final Color? suffixIconColor;
/// The style to use for the [InputDecoration.counterText].
///
/// If [counterStyle] is a [MaterialStateTextStyle], then the effective
/// text style can depend on the [MaterialState.focused] state, i.e.
/// if the [TextField] is focused or not.
///
/// If null, defaults to the [helperStyle].
final TextStyle? counterStyle;
......@@ -3890,6 +4102,10 @@ class InputDecorationTheme with Diagnosticable {
/// The shape of the border to draw around the decoration's container.
///
/// If [border] is a [MaterialStateUnderlineInputBorder]
/// or [MaterialStateOutlineInputBorder], then the effective border can depend on
/// the [MaterialState.focused] state, i.e. if the [TextField] is focused or not.
///
/// The decoration's container is the area which is filled if [filled] is
/// true and bordered per the [border]. It's the area adjacent to
/// [InputDecoration.icon] and above the widgets that contain
......@@ -3954,8 +4170,11 @@ class InputDecorationTheme with Diagnosticable {
bool? isDense,
EdgeInsetsGeometry? contentPadding,
bool? isCollapsed,
Color? iconColor,
TextStyle? prefixStyle,
Color? prefixIconColor,
TextStyle? suffixStyle,
Color? suffixIconColor,
TextStyle? counterStyle,
bool? filled,
Color? fillColor,
......@@ -3981,9 +4200,12 @@ class InputDecorationTheme with Diagnosticable {
floatingLabelBehavior: floatingLabelBehavior ?? this.floatingLabelBehavior,
isDense: isDense ?? this.isDense,
contentPadding: contentPadding ?? this.contentPadding,
iconColor: iconColor,
isCollapsed: isCollapsed ?? this.isCollapsed,
prefixStyle: prefixStyle ?? this.prefixStyle,
prefixIconColor: prefixIconColor ?? this.prefixIconColor,
suffixStyle: suffixStyle ?? this.suffixStyle,
suffixIconColor: suffixIconColor ?? this.suffixIconColor,
counterStyle: counterStyle ?? this.counterStyle,
filled: filled ?? this.filled,
fillColor: fillColor ?? this.fillColor,
......@@ -4014,8 +4236,11 @@ class InputDecorationTheme with Diagnosticable {
isDense,
contentPadding,
isCollapsed,
iconColor,
prefixStyle,
prefixIconColor,
suffixStyle,
suffixIconColor,
counterStyle,
filled,
fillColor,
......@@ -4033,7 +4258,7 @@ class InputDecorationTheme with Diagnosticable {
}
@override
bool operator==(Object other) {
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
......@@ -4049,8 +4274,11 @@ class InputDecorationTheme with Diagnosticable {
&& other.isDense == isDense
&& other.contentPadding == contentPadding
&& other.isCollapsed == isCollapsed
&& other.iconColor == iconColor
&& other.prefixStyle == prefixStyle
&& other.prefixIconColor == prefixIconColor
&& other.suffixStyle == suffixStyle
&& other.suffixIconColor == suffixIconColor
&& other.counterStyle == counterStyle
&& other.floatingLabelBehavior == floatingLabelBehavior
&& other.filled == filled
......@@ -4083,7 +4311,10 @@ class InputDecorationTheme with Diagnosticable {
properties.add(DiagnosticsProperty<bool>('isDense', isDense, defaultValue: defaultTheme.isDense));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('contentPadding', contentPadding, defaultValue: defaultTheme.contentPadding));
properties.add(DiagnosticsProperty<bool>('isCollapsed', isCollapsed, defaultValue: defaultTheme.isCollapsed));
properties.add(DiagnosticsProperty<Color>('iconColor', iconColor, defaultValue: defaultTheme.iconColor));
properties.add(DiagnosticsProperty<Color>('prefixIconColor', prefixIconColor, defaultValue: defaultTheme.prefixIconColor));
properties.add(DiagnosticsProperty<TextStyle>('prefixStyle', prefixStyle, defaultValue: defaultTheme.prefixStyle));
properties.add(DiagnosticsProperty<Color>('suffixIconColor', suffixIconColor, defaultValue: defaultTheme.suffixIconColor));
properties.add(DiagnosticsProperty<TextStyle>('suffixStyle', suffixStyle, defaultValue: defaultTheme.suffixStyle));
properties.add(DiagnosticsProperty<TextStyle>('counterStyle', counterStyle, defaultValue: defaultTheme.counterStyle));
properties.add(DiagnosticsProperty<bool>('filled', filled, defaultValue: defaultTheme.filled));
......
......@@ -6,6 +6,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'input_border.dart';
/// Interactive states that some of the Material widgets can take on when
/// receiving input from the user.
///
......@@ -96,6 +98,9 @@ typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);
/// [Set] of [MaterialState]s, like [MaterialState.pressed],
/// [MaterialState.focused] and [MaterialState.hovered].
///
/// [MaterialStateColor] should only be used with widgets that document
/// their support, like [TimePickerThemeData.dayPeriodColor].
///
/// To use a [MaterialStateColor], you can either:
/// 1. Create a subclass of [MaterialStateColor] and implement the abstract `resolve` method.
/// 2. Use [MaterialStateColor.resolveWith] and pass in a callback that
......@@ -110,11 +115,6 @@ typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
///
/// This class enables existing widget implementations with [Color]
/// properties to be extended to also effectively support `MaterialStateProperty<Color>`
/// property values. [MaterialStateColor] should only be used with widgets that document
/// their support, like [TimePickerThemeData.dayPeriodColor].
///
/// {@tool snippet}
///
/// This example defines a `MaterialStateColor` with a const constructor.
......@@ -391,6 +391,196 @@ abstract class MaterialStateOutlinedBorder extends OutlinedBorder implements Mat
OutlinedBorder? resolve(Set<MaterialState> states);
}
/// Defines a [TextStyle] that is also a [MaterialStateProperty].
///
/// This class exists to enable widgets with [TextStyle] valued properties
/// to also accept [MaterialStateProperty<TextStyle>] values. A material
/// state text style property represents a text style which depends on
/// a widget's "interactive state". This state is represented as a
/// [Set] of [MaterialState]s, like [MaterialState.pressed],
/// [MaterialState.focused] and [MaterialState.hovered].
///
/// [MaterialStateTextStyle] should only be used with widgets that document
/// their support, like [InputDecoration.labelStyle].
///
/// To use a [MaterialStateTextStyle], you can either:
/// 1. Create a subclass of [MaterialStateTextStyle] and implement the abstract `resolve` method.
/// 2. Use [MaterialStateTextStyle.resolveWith] and pass in a callback that
/// will be used to resolve the color in the given states.
///
/// If a [MaterialStateTextStyle] is used for a property or a parameter that doesn't
/// support resolving [MaterialStateProperty<TextStyle>]s, then its default color
/// value will be used for all states.
///
/// To define a `const` [MaterialStateTextStyle], you'll need to extend
/// [MaterialStateTextStyle] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
abstract class MaterialStateTextStyle extends TextStyle implements MaterialStateProperty<TextStyle> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateTextStyle();
/// Creates a [MaterialStateTextStyle] from a [MaterialPropertyResolver<TextStyle>]
/// callback function.
///
/// If used as a regular text style, the style resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
static MaterialStateTextStyle resolveWith(MaterialPropertyResolver<TextStyle> callback) =>
_MaterialStateTextStyle(callback);
/// Returns a [TextStyle] that's to be used when a Material component is in the
/// specified state.
@override
TextStyle resolve(Set<MaterialState> states);
}
/// A [MaterialStateTextStyle] created from a [MaterialPropertyResolver<TextStyle>]
/// callback alone.
///
/// If used as a regular text style, the style resolved in the default state will
/// be used.
///
/// Used by [MaterialStateTextStyle.resolveWith].
class _MaterialStateTextStyle extends MaterialStateTextStyle {
const _MaterialStateTextStyle(this._resolve);
final MaterialPropertyResolver<TextStyle> _resolve;
@override
TextStyle resolve(Set<MaterialState> states) => _resolve(states);
}
/// Defines a [OutlineInputBorder] that is also a [MaterialStateProperty].
///
/// This class exists to enable widgets with [OutlineInputBorder] valued properties
/// to also accept [MaterialStateProperty<OutlineInputBorder>] values. A material
/// state input border property represents a text style which depends on
/// a widget's "interactive state". This state is represented as a
/// [Set] of [MaterialState]s, like [MaterialState.pressed],
/// [MaterialState.focused] and [MaterialState.hovered].
///
/// [MaterialStateOutlineInputBorder] should only be used with widgets that document
/// their support, like [InputDecoration.border].
///
/// To use a [MaterialStateOutlineInputBorder], you can either:
/// 1. Create a subclass of [MaterialStateOutlineInputBorder] and implement the abstract `resolve` method.
/// 2. Use [MaterialStateOutlineInputBorder.resolveWith] and pass in a callback that
/// will be used to resolve the color in the given states.
///
/// If a [MaterialStateOutlineInputBorder] is used for a property or a parameter that doesn't
/// support resolving [MaterialStateProperty<OutlineInputBorder>]s, then its default color
/// value will be used for all states.
///
/// To define a `const` [MaterialStateOutlineInputBorder], you'll need to extend
/// [MaterialStateOutlineInputBorder] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
abstract class MaterialStateOutlineInputBorder extends OutlineInputBorder implements MaterialStateProperty<InputBorder> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateOutlineInputBorder();
/// Creates a [MaterialStateOutlineInputBorder] from a [MaterialPropertyResolver<InputBorder>]
/// callback function.
///
/// If used as a regular input border, the border resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
static MaterialStateOutlineInputBorder resolveWith(MaterialPropertyResolver<InputBorder> callback) =>
_MaterialStateOutlineInputBorder(callback);
/// Returns a [InputBorder] that's to be used when a Material component is in the
/// specified state.
@override
InputBorder resolve(Set<MaterialState> states);
}
/// A [MaterialStateOutlineInputBorder] created from a [MaterialPropertyResolver<OutlineInputBorder>]
/// callback alone.
///
/// If used as a regular input border, the border resolved in the default state will
/// be used.
///
/// Used by [MaterialStateTextStyle.resolveWith].
class _MaterialStateOutlineInputBorder extends MaterialStateOutlineInputBorder {
const _MaterialStateOutlineInputBorder(this._resolve);
final MaterialPropertyResolver<InputBorder> _resolve;
@override
InputBorder resolve(Set<MaterialState> states) => _resolve(states);
}
/// Defines a [UnderlineInputBorder] that is also a [MaterialStateProperty].
///
/// This class exists to enable widgets with [UnderlineInputBorder] valued properties
/// to also accept [MaterialStateProperty<UnderlineInputBorder>] values. A material
/// state input border property represents a text style which depends on
/// a widget's "interactive state". This state is represented as a
/// [Set] of [MaterialState]s, like [MaterialState.pressed],
/// [MaterialState.focused] and [MaterialState.hovered].
///
/// [MaterialStateUnderlineInputBorder] should only be used with widgets that document
/// their support, like [InputDecoration.border].
///
/// To use a [MaterialStateUnderlineInputBorder], you can either:
/// 1. Create a subclass of [MaterialStateUnderlineInputBorder] and implement the abstract `resolve` method.
/// 2. Use [MaterialStateUnderlineInputBorder.resolveWith] and pass in a callback that
/// will be used to resolve the color in the given states.
///
/// If a [MaterialStateUnderlineInputBorder] is used for a property or a parameter that doesn't
/// support resolving [MaterialStateProperty<UnderlineInputBorder>]s, then its default color
/// value will be used for all states.
///
/// To define a `const` [MaterialStateUnderlineInputBorder], you'll need to extend
/// [MaterialStateUnderlineInputBorder] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
abstract class MaterialStateUnderlineInputBorder extends UnderlineInputBorder implements MaterialStateProperty<InputBorder> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateUnderlineInputBorder();
/// Creates a [MaterialStateUnderlineInputBorder] from a [MaterialPropertyResolver<InputBorder>]
/// callback function.
///
/// If used as a regular ionput bortder, the border resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
static MaterialStateUnderlineInputBorder resolveWith(MaterialPropertyResolver<InputBorder> callback) =>
_MaterialStateUnderlineInputBorder(callback);
/// Returns a [InputBorder] that's to be used when a Material component is in the
/// specified state.
@override
InputBorder resolve(Set<MaterialState> states);
}
/// A [MaterialStateUnderlineInputBorder] created from a [MaterialPropertyResolver<UnderlineInputBorder>]
/// callback alone.
///
/// If used as a regular input border, the border resolved in the default state will
/// be used.
///
/// Used by [MaterialStateTextStyle.resolveWith].
class _MaterialStateUnderlineInputBorder extends MaterialStateUnderlineInputBorder {
const _MaterialStateUnderlineInputBorder(this._resolve);
final MaterialPropertyResolver<InputBorder> _resolve;
@override
InputBorder resolve(Set<MaterialState> states) => _resolve(states);
}
/// Interface for classes that [resolve] to a value of type `T` based
/// on a widget's interactive "state", which is defined as a set
/// of [MaterialState]s.
......
......@@ -3642,6 +3642,110 @@ void main() {
expect(decoration.constraints, const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40));
});
testWidgets('InputDecorationTheme.inputDecoration with MaterialState', (WidgetTester tester) async {
final MaterialStateTextStyle themeStyle = MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
return const TextStyle(color: Colors.green);
});
final MaterialStateTextStyle decorationStyle = MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {
return const TextStyle(color: Colors.blue);
});
// InputDecorationTheme arguments define InputDecoration properties.
InputDecoration decoration = const InputDecoration().applyDefaults(
InputDecorationTheme(
labelStyle: themeStyle,
helperStyle: themeStyle,
hintStyle: themeStyle,
errorStyle: themeStyle,
isDense: true,
contentPadding: const EdgeInsets.all(1.0),
prefixStyle: themeStyle,
suffixStyle: themeStyle,
counterStyle: themeStyle,
filled: true,
fillColor: Colors.red,
focusColor: Colors.blue,
border: InputBorder.none,
alignLabelWithHint: true,
constraints: const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
),
);
expect(decoration.labelStyle, themeStyle);
expect(decoration.helperStyle, themeStyle);
expect(decoration.hintStyle, themeStyle);
expect(decoration.errorStyle, themeStyle);
expect(decoration.isDense, true);
expect(decoration.contentPadding, const EdgeInsets.all(1.0));
expect(decoration.prefixStyle, themeStyle);
expect(decoration.suffixStyle, themeStyle);
expect(decoration.counterStyle, themeStyle);
expect(decoration.filled, true);
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
final MaterialStateOutlineInputBorder border = MaterialStateOutlineInputBorder.resolveWith((Set<MaterialState> states) {
return const OutlineInputBorder();
});
decoration = InputDecoration(
labelStyle: decorationStyle,
helperStyle: decorationStyle,
hintStyle: decorationStyle,
errorStyle: decorationStyle,
isDense: false,
contentPadding: const EdgeInsets.all(4.0),
prefixStyle: decorationStyle,
suffixStyle: decorationStyle,
counterStyle: decorationStyle,
filled: false,
fillColor: Colors.blue,
border: border,
alignLabelWithHint: false,
constraints: const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40),
).applyDefaults(
InputDecorationTheme(
labelStyle: themeStyle,
helperStyle: themeStyle,
helperMaxLines: 5,
hintStyle: themeStyle,
errorStyle: themeStyle,
errorMaxLines: 4,
isDense: true,
contentPadding: const EdgeInsets.all(1.0),
prefixStyle: themeStyle,
suffixStyle: themeStyle,
counterStyle: themeStyle,
filled: true,
fillColor: Colors.red,
focusColor: Colors.blue,
border: InputBorder.none,
alignLabelWithHint: true,
constraints: const BoxConstraints(minWidth: 40, maxWidth: 30, minHeight: 20, maxHeight: 10),
),
);
expect(decoration.labelStyle, decorationStyle);
expect(decoration.helperStyle, decorationStyle);
expect(decoration.helperMaxLines, 5);
expect(decoration.hintStyle, decorationStyle);
expect(decoration.errorStyle, decorationStyle);
expect(decoration.errorMaxLines, 4);
expect(decoration.isDense, false);
expect(decoration.contentPadding, const EdgeInsets.all(4.0));
expect(decoration.prefixStyle, decorationStyle);
expect(decoration.suffixStyle, decorationStyle);
expect(decoration.counterStyle, decorationStyle);
expect(decoration.filled, false);
expect(decoration.fillColor, Colors.blue);
expect(decoration.border, isA<MaterialStateOutlineInputBorder>());
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 {
// This is a regression test for https://github.com/flutter/flutter/issues/15742
......
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