Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
677f218e
Unverified
Commit
677f218e
authored
Jan 20, 2018
by
Hans Muller
Committed by
GitHub
Jan 20, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added InputDecorationTheme (#14177)
parent
38ce59f0
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
539 additions
and
61 deletions
+539
-61
input_border.dart
packages/flutter/lib/src/material/input_border.dart
+44
-0
input_decorator.dart
packages/flutter/lib/src/material/input_decorator.dart
+238
-41
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+4
-2
text_form_field.dart
packages/flutter/lib/src/material/text_form_field.dart
+4
-1
theme_data.dart
packages/flutter/lib/src/material/theme_data.dart
+17
-0
input_decorator_test.dart
packages/flutter/test/material/input_decorator_test.dart
+232
-17
No files found.
packages/flutter/lib/src/material/input_border.dart
View file @
677f218e
...
...
@@ -28,6 +28,13 @@ import 'package:flutter/widgets.dart';
/// rounded rectangle around the input decorator's container.
/// * [InputDecoration], which is used to configure an [InputDecorator].
abstract
class
InputBorder
extends
ShapeBorder
{
/// No input border.
///
/// Use this value with [InputDecoration.border] to specify that no border
/// should be drawn. The [InputDecoration.collapsed] constructor sets
/// its border to this value.
static
const
InputBorder
none
=
const
_NoInputBorder
();
/// Creates a border for an [InputDecorator].
///
/// The [borderSide] parameter must not be null. Applications typically do
...
...
@@ -72,6 +79,43 @@ abstract class InputBorder extends ShapeBorder {
});
}
// Used to create the InputBorder.none singleton.
class
_NoInputBorder
extends
InputBorder
{
const
_NoInputBorder
()
:
super
(
borderSide:
BorderSide
.
none
);
@override
_NoInputBorder
copyWith
({
BorderSide
borderSide
})
=>
const
_NoInputBorder
();
@override
bool
get
isOutline
=>
false
;
@override
EdgeInsetsGeometry
get
dimensions
=>
EdgeInsets
.
zero
;
@override
_NoInputBorder
scale
(
double
t
)
=>
const
_NoInputBorder
();
@override
Path
getInnerPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
return
new
Path
()..
addRect
(
rect
);
}
@override
Path
getOuterPath
(
Rect
rect
,
{
TextDirection
textDirection
})
{
return
new
Path
()..
addRect
(
rect
);
}
@override
void
paint
(
Canvas
canvas
,
Rect
rect
,
{
double
gapStart
,
double
gapExtent:
0.0
,
double
gapPercentage:
0.0
,
TextDirection
textDirection
,
})
{
// Do not paint.
}
}
/// Draws a horizontal line at the bottom of an [InputDecorator]'s container.
///
/// The input decorator's "container" is the optionally filled area above the
...
...
packages/flutter/lib/src/material/input_decorator.dart
View file @
677f218e
...
...
@@ -402,6 +402,7 @@ enum _DecorationSlot {
class
_Decoration
{
const
_Decoration
({
@required
this
.
contentPadding
,
@required
this
.
isCollapsed
,
@required
this
.
floatingLabelHeight
,
@required
this
.
floatingLabelProgress
,
this
.
border
,
...
...
@@ -418,10 +419,12 @@ class _Decoration {
this
.
counter
,
this
.
container
,
})
:
assert
(
contentPadding
!=
null
),
assert
(
isCollapsed
!=
null
),
assert
(
floatingLabelHeight
!=
null
),
assert
(
floatingLabelProgress
!=
null
);
final
EdgeInsets
contentPadding
;
final
bool
isCollapsed
;
final
double
floatingLabelHeight
;
final
double
floatingLabelProgress
;
final
InputBorder
border
;
...
...
@@ -895,9 +898,9 @@ class _RenderDecoration extends RenderBox {
final
double
right
=
overallWidth
-
contentPadding
.
right
;
height
=
layout
.
containerHeight
;
baseline
=
decoration
.
border
==
null
||
decoration
.
border
.
isOutline
?
layout
.
outline
Baseline
:
layout
.
input
Baseline
;
baseline
=
decoration
.
isCollapsed
||
!
decoration
.
border
.
isOutline
?
layout
.
input
Baseline
:
layout
.
outline
Baseline
;
if
(
icon
!=
null
)
{
final
double
x
=
textDirection
==
TextDirection
.
rtl
?
overallWidth
-
icon
.
size
.
width
:
0.0
;
...
...
@@ -1250,7 +1253,7 @@ class InputDecorator extends StatefulWidget {
/// The [isFocused] and [isEmpty] arguments must not be null.
const
InputDecorator
({
Key
key
,
@required
this
.
decoration
,
this
.
decoration
,
this
.
baseStyle
,
this
.
textAlign
,
this
.
isFocused
:
false
,
...
...
@@ -1261,12 +1264,17 @@ class InputDecorator extends StatefulWidget {
super
(
key:
key
);
/// The text and styles to use when decorating the child.
///
/// If null, `const InputDecoration()` is used. Null [InputDecoration]
/// properties are initialized with the corresponding values from
/// [ThemeData.inputDecorationTheme].
final
InputDecoration
decoration
;
/// The style on which to base the label, hint, counter, and error styles
/// if the [decoration] does not provide explicit styles.
///
/// If null, defaults to a text style from the current [Theme].
/// If null, `baseStyle` defaults to the `subhead` style from the
/// current [Theme], see [ThemeData.textTheme].
final
TextStyle
baseStyle
;
/// How the text in the decoration should be aligned horizontally.
...
...
@@ -1341,6 +1349,12 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
);
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
_effectiveDecoration
=
null
;
}
@override
void
dispose
()
{
_floatingLabelController
.
dispose
();
...
...
@@ -1354,7 +1368,14 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
});
}
InputDecoration
get
decoration
=>
widget
.
decoration
;
InputDecoration
_effectiveDecoration
;
InputDecoration
get
decoration
{
_effectiveDecoration
??=
widget
.
decoration
.
applyDefaults
(
Theme
.
of
(
context
).
inputDecorationTheme
);
return
_effectiveDecoration
;
}
TextAlign
get
textAlign
=>
widget
.
textAlign
;
bool
get
isFocused
=>
widget
.
isFocused
;
bool
get
isEmpty
=>
widget
.
isEmpty
;
...
...
@@ -1362,6 +1383,9 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
@override
void
didUpdateWidget
(
InputDecorator
old
)
{
super
.
didUpdateWidget
(
old
);
if
(
widget
.
decoration
!=
old
.
decoration
)
_effectiveDecoration
=
null
;
if
(
widget
.
_labelIsFloating
!=
old
.
_labelIsFloating
)
{
if
(
widget
.
_labelIsFloating
)
_floatingLabelController
.
forward
();
...
...
@@ -1392,7 +1416,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
}
Color
_getFillColor
(
ThemeData
themeData
)
{
if
(
!
decoration
.
filled
)
if
(
decoration
.
filled
!=
true
)
// filled == null same as filled == false
return
Colors
.
transparent
;
if
(
decoration
.
fillColor
!=
null
)
return
decoration
.
fillColor
;
...
...
@@ -1418,12 +1442,11 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
// then the label appears where the hint would.
bool
get
_hasInlineLabel
=>
!
isFocused
&&
isEmpty
&&
decoration
.
labelText
!=
null
;
// The
style for the inline label or hint when they're displayed "inline", i.e.
// when they appear in place of the empty text field.
TextStyle
_getInline
Label
Style
(
ThemeData
themeData
)
{
// The
base style for the inline label or hint when they're displayed "inline",
//
i.e.
when they appear in place of the empty text field.
TextStyle
_getInlineStyle
(
ThemeData
themeData
)
{
return
themeData
.
textTheme
.
subhead
.
merge
(
widget
.
baseStyle
)
.
copyWith
(
color:
themeData
.
hintColor
)
.
merge
(
decoration
.
hintStyle
);
.
copyWith
(
color:
themeData
.
hintColor
);
}
TextStyle
_getFloatingLabelStyle
(
ThemeData
themeData
)
{
...
...
@@ -1445,7 +1468,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
}
double
get
_borderWeight
{
if
(
decoration
.
isCollapsed
||
decoration
.
border
==
null
||
!
decoration
.
enabled
)
if
(
decoration
.
isCollapsed
||
decoration
.
border
==
InputBorder
.
none
||
!
decoration
.
enabled
)
return
0.0
;
return
isFocused
?
2.0
:
1.0
;
}
...
...
@@ -1459,21 +1482,22 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
@override
Widget
build
(
BuildContext
context
)
{
final
ThemeData
themeData
=
Theme
.
of
(
context
);
final
TextStyle
inlineStyle
=
_getInline
Label
Style
(
themeData
);
final
TextStyle
inlineStyle
=
_getInlineStyle
(
themeData
);
final
TextStyle
hintStyle
=
inlineStyle
.
merge
(
decoration
.
hintStyle
);
final
Widget
hint
=
decoration
.
hintText
==
null
?
null
:
new
AnimatedOpacity
(
opacity:
(
isEmpty
&&
!
_hasInlineLabel
)
?
1.0
:
0.0
,
duration:
_kTransitionDuration
,
curve:
_kTransitionCurve
,
child:
new
Text
(
decoration
.
hintText
,
style:
inline
Style
,
style:
hint
Style
,
overflow:
TextOverflow
.
ellipsis
,
textAlign:
textAlign
,
),
);
final
InputBorder
border
=
decoration
.
border
==
null
?
null
:
decoration
.
border
.
copyWith
(
final
InputBorder
border
=
decoration
.
border
.
copyWith
(
borderSide:
new
BorderSide
(
color:
_getBorderColor
(
themeData
),
width:
_borderWeight
,
...
...
@@ -1490,6 +1514,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
child:
containerFill
,
);
final
TextStyle
inlineLabelStyle
=
inlineStyle
.
merge
(
decoration
.
labelStyle
);
final
Widget
label
=
decoration
.
labelText
==
null
?
null
:
new
_Shaker
(
animation:
_shakingLabelController
.
view
,
child:
new
AnimatedDefaultTextStyle
(
...
...
@@ -1497,7 +1522,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
curve:
_kTransitionCurve
,
style:
widget
.
_labelIsFloating
?
_getFloatingLabelStyle
(
themeData
)
:
_getInlineLabelStyle
(
themeData
)
,
:
inlineLabelStyle
,
child:
new
Text
(
decoration
.
labelText
,
overflow:
TextOverflow
.
ellipsis
,
...
...
@@ -1513,7 +1538,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
opacity:
widget
.
_labelIsFloating
?
1.0
:
0.0
,
child:
new
Text
(
decoration
.
prefixText
,
style:
decoration
.
prefixStyle
??
inline
Style
style:
decoration
.
prefixStyle
??
hint
Style
),
);
...
...
@@ -1524,12 +1549,13 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
opacity:
widget
.
_labelIsFloating
?
1.0
:
0.0
,
child:
new
Text
(
decoration
.
suffixText
,
style:
decoration
.
suffixStyle
??
inline
Style
style:
decoration
.
suffixStyle
??
hint
Style
),
);
final
Color
activeColor
=
_getActiveColor
(
themeData
);
final
double
iconSize
=
decoration
.
isDense
?
18.0
:
24.0
;
final
bool
decorationIsDense
=
decoration
.
isDense
==
true
;
// isDense == null, same as false
final
double
iconSize
=
decorationIsDense
?
18.0
:
24.0
;
final
Color
iconColor
=
isFocused
?
activeColor
:
Colors
.
black45
;
final
Widget
icon
=
decoration
.
icon
==
null
?
null
:
...
...
@@ -1583,24 +1609,24 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
if
(
decoration
.
isCollapsed
)
{
floatingLabelHeight
=
0.0
;
contentPadding
=
decoration
.
contentPadding
??
EdgeInsets
.
zero
;
}
else
if
(
decoration
.
border
==
null
||
!
decoration
.
border
.
isOutline
)
{
}
else
if
(!
decoration
.
border
.
isOutline
)
{
// 4.0: the vertical gap between the inline elements and the floating label.
floatingLabelHeight
=
4.0
+
0.75
*
inlineStyle
.
fontSize
;
if
(
decoration
.
filled
)
{
contentPadding
=
decoration
.
contentPadding
??
(
decoration
.
i
sDense
floatingLabelHeight
=
4.0
+
0.75
*
inline
Label
Style
.
fontSize
;
if
(
decoration
.
filled
==
true
)
{
// filled == null same as filled == false
contentPadding
=
decoration
.
contentPadding
??
(
decoration
I
sDense
?
const
EdgeInsets
.
fromLTRB
(
12.0
,
8.0
,
12.0
,
8.0
)
:
const
EdgeInsets
.
fromLTRB
(
12.0
,
12.0
,
12.0
,
12.0
));
}
else
{
// Not left or right padding for underline borders that aren't filled
// is a small concession to backwards compatibility. This eliminates
// the most noticeable layout change introduced by #13734.
contentPadding
=
decoration
.
contentPadding
??
(
decoration
.
i
sDense
contentPadding
=
decoration
.
contentPadding
??
(
decoration
I
sDense
?
const
EdgeInsets
.
fromLTRB
(
0.0
,
8.0
,
0.0
,
8.0
)
:
const
EdgeInsets
.
fromLTRB
(
0.0
,
12.0
,
0.0
,
12.0
));
}
}
else
{
floatingLabelHeight
=
0.0
;
contentPadding
=
decoration
.
contentPadding
??
(
decoration
.
i
sDense
contentPadding
=
decoration
.
contentPadding
??
(
decoration
I
sDense
?
const
EdgeInsets
.
fromLTRB
(
12.0
,
20.0
,
12.0
,
12.0
)
:
const
EdgeInsets
.
fromLTRB
(
12.0
,
24.0
,
12.0
,
16.0
));
}
...
...
@@ -1608,6 +1634,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
return
new
_Decorator
(
decoration:
new
_Decoration
(
contentPadding:
contentPadding
,
isCollapsed:
decoration
.
isCollapsed
,
floatingLabelHeight:
floatingLabelHeight
,
floatingLabelProgress:
_floatingLabelController
.
value
,
border:
decoration
.
border
,
...
...
@@ -1648,8 +1675,12 @@ class InputDecoration {
/// Creates a bundle of the border, labels, icons, and styles used to
/// decorate a Material Design text field.
///
/// The [isDense], [filled], and [enabled] arguments must not
/// be null.
/// Unless specified by [ThemeData.inputDecorationTheme],
/// [InputDecorator] defaults [isDense] to true, and [filled] to false,
/// Similarly, the default border is an instance of [UnderlineInputBorder].
/// If [border] is [InputBorder.none] then no border is drawn.
///
/// The [enabled] argument must not be null.
const
InputDecoration
({
this
.
icon
,
this
.
labelText
,
...
...
@@ -1660,7 +1691,7 @@ class InputDecoration {
this
.
hintStyle
,
this
.
errorText
,
this
.
errorStyle
,
this
.
isDense
:
false
,
this
.
isDense
,
this
.
contentPadding
,
this
.
prefixIcon
,
this
.
prefixText
,
...
...
@@ -1670,18 +1701,15 @@ class InputDecoration {
this
.
suffixStyle
,
this
.
counterText
,
this
.
counterStyle
,
this
.
filled
:
false
,
this
.
filled
,
this
.
fillColor
,
this
.
border
:
const
UnderlineInputBorder
()
,
this
.
border
,
this
.
enabled
:
true
,
})
:
assert
(
isDense
!=
null
),
assert
(
filled
!=
null
),
assert
(
enabled
!=
null
),
isCollapsed
=
false
;
})
:
assert
(
enabled
!=
null
),
isCollapsed
=
false
;
/// Defines an [InputDecorator] that is the same size as the input field.
///
/// This type of input decoration
only includes the border
.
/// This type of input decoration
does not include a border by default
.
///
/// Sets the [isCollapsed] property to true.
const
InputDecoration
.
collapsed
({
...
...
@@ -1689,10 +1717,9 @@ class InputDecoration {
this
.
hintStyle
,
this
.
filled
:
false
,
this
.
fillColor
,
this
.
border
:
const
UnderlineInputBorder
()
,
this
.
border
:
InputBorder
.
none
,
this
.
enabled
:
true
,
})
:
assert
(
filled
!=
null
),
assert
(
enabled
!=
null
),
})
:
assert
(
enabled
!=
null
),
icon
=
null
,
labelText
=
null
,
labelStyle
=
null
,
...
...
@@ -1993,6 +2020,28 @@ class InputDecoration {
);
}
/// Used by widgets like [TextField] and [InputDecorator] to create a new
/// [InputDecoration] with default values taken from the [theme].
///
/// Only null valued properties from this [InputDecoration] are replaced
/// by the corresponding values from [theme].
InputDecoration
applyDefaults
(
InputDecorationTheme
theme
)
{
return
copyWith
(
labelStyle:
labelStyle
??
theme
.
labelStyle
,
helperStyle:
helperStyle
??
theme
.
helperStyle
,
hintStyle:
hintStyle
??
theme
.
hintStyle
,
errorStyle:
errorStyle
??
theme
.
errorStyle
,
isDense:
isDense
??
theme
.
isDense
,
contentPadding:
contentPadding
??
theme
.
contentPadding
,
prefixStyle:
prefixStyle
??
theme
.
prefixStyle
,
suffixStyle:
suffixStyle
??
theme
.
suffixStyle
,
counterStyle:
counterStyle
??
theme
.
counterStyle
,
filled:
filled
??
theme
.
filled
,
fillColor:
fillColor
??
theme
.
fillColor
,
border:
border
??
theme
.
border
,
);
}
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
...
...
@@ -2071,7 +2120,7 @@ class InputDecoration {
description
.
add
(
'hintText: "
$hintText
"'
);
if
(
errorText
!=
null
)
description
.
add
(
'errorText: "
$errorText
"'
);
if
(
isDense
)
if
(
isDense
??
false
)
description
.
add
(
'isDense:
$isDense
'
);
if
(
contentPadding
!=
null
)
description
.
add
(
'contentPadding:
$contentPadding
'
);
...
...
@@ -2093,7 +2142,7 @@ class InputDecoration {
description
.
add
(
'counterText:
$counterText
'
);
if
(
counterStyle
!=
null
)
description
.
add
(
'counterStyle:
$counterStyle
'
);
if
(
filled
)
if
(
filled
==
true
)
// filled == null same as filled == false
description
.
add
(
'filled: true'
);
if
(
fillColor
!=
null
)
description
.
add
(
'fillColor:
$fillColor
'
);
...
...
@@ -2104,3 +2153,151 @@ class InputDecoration {
return
'InputDecoration(
${description.join(', ')}
)'
;
}
}
/// Defines the default appearance of [InputDecorator]s.
///
/// This class is used to define the value of [ThemeData.inputDecorationTheme].
/// The [InputDecorator], [TextField], and [TextFormField] widgets use
/// the current input decoration theme to initialize null [InputDecoration]
/// properties.
///
/// The [InputDecoration.applyDefaults] method is used to combine a input
/// decoration theme with an [InputDecoration] object.
@immutable
class
InputDecorationTheme
{
/// Creates a value for [ThemeData.inputDecorationTheme] that
/// defines default values for [InputDecorator].
///
/// The values of [isDense], [isCollapsed], [isFilled], and [border] must
/// not be null.
const
InputDecorationTheme
({
this
.
labelStyle
,
this
.
helperStyle
,
this
.
hintStyle
,
this
.
errorStyle
,
this
.
isDense
:
false
,
this
.
contentPadding
,
this
.
isCollapsed
:
false
,
this
.
prefixStyle
,
this
.
suffixStyle
,
this
.
counterStyle
,
this
.
filled
:
false
,
this
.
fillColor
,
this
.
border
:
const
UnderlineInputBorder
(),
})
:
assert
(
isDense
!=
null
),
assert
(
isCollapsed
!=
null
),
assert
(
filled
!=
null
),
assert
(
border
!=
null
);
/// The style to use for [InputDecoration.labelText] when the label is
/// above (i.e., vertically adjacent to) the input field.
///
/// When the [labelText] is on top of the input field, the text uses the
/// [hintStyle] instead.
///
/// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
final
TextStyle
labelStyle
;
/// The style to use for [InputDecoration.helperText].
final
TextStyle
helperStyle
;
/// The style to use for the [InputDecoration.hintText].
///
/// 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 my be entered in the input field).
///
/// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
final
TextStyle
hintStyle
;
/// The style to use for the [InputDecoration.errorText].
///
/// If null, defaults of a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
final
TextStyle
errorStyle
;
/// Whether the input decorator's child is part of a dense form (i.e., uses
/// less vertical space).
///
/// Defaults to false.
final
bool
isDense
;
/// The padding for the input decoration's container.
///
/// The decoration's container is the area which is filled if
/// [InputDecoration.isFilled] is true and bordered per the [border].
/// It's the area adjacent to [InputDecoration.icon] and above the
/// [InputDecoration.icon] and above the widgets that contain
/// [InputDecoration.helperText], [InputDecoration.errorText], and
/// [InputDecoration.counterText].
///
/// By default the `contentPadding` reflects [isDense] and the type of the
/// [border]. If [isCollapsed] is true then `contentPadding` is
/// [EdgeInsets.zero].
final
EdgeInsets
contentPadding
;
/// Whether the decoration is the same size as the input field.
///
/// A collapsed decoration cannot have [InputDecoration.labelText],
/// [InputDecoration.errorText], or an [InputDecoration.icon].
final
bool
isCollapsed
;
/// The style to use for the [InputDecoration.prefixText].
///
/// If null, defaults to the [hintStyle].
final
TextStyle
prefixStyle
;
/// The style to use for the [InputDecoration.suffixText].
///
/// If null, defaults to the [hintStyle].
final
TextStyle
suffixStyle
;
/// The style to use for the [InputDecoration.counterText].
///
/// If null, defaults to the [helperStyle].
final
TextStyle
counterStyle
;
/// If true the decoration's container is filled with [fillColor].
///
/// Typically this field set to true if [border] is
/// [const UnderlineInputBorder()].
///
/// The decoration's container is the area, defined by the border's
/// [InputBorder.getOuterPath], which is filled if [isFilled] is
/// true and bordered per the [border].
///
/// This property is false by default.
final
bool
filled
;
/// The color to fill the decoration's container with, if [filled] is true.
///
/// By default the fillColor is based on the current [Theme].
///
/// The decoration's container is the area, defined by the border's
/// [InputBorder.getOuterPath], which is filled if [isFilled] is
/// true and bordered per the [border].
final
Color
fillColor
;
/// The border to draw around the decoration's container.
///
/// The decoration's container is the area which is filled if [isFilled] is
/// true and bordered per the [border]. It's the area adjacent to
/// [InputDecoration.icon] and above the widgets that contain
/// [InputDecoration.helperText], [InputDecoration.errorText], and
/// [InputDecoration.counterText].
///
/// The default value of this property is `const UnderlineInputBorder()`.
///
/// The border's bounds, i.e. the value of `border.getOuterPath()`, defines
/// the area to be filled.
///
/// See also:
/// * [InputBorder.none], which doesn't draw a border.
/// * [UnderlineInputBorder], which draws a horizontal line at the
/// bottom of the input decorator's container.
/// * [OutlineInputBorder], an [InputDecorator] border which draws a
/// rounded rectangle around the input decorator's container.
final
InputBorder
border
;
}
packages/flutter/lib/src/material/text_field.dart
View file @
677f218e
...
...
@@ -296,10 +296,12 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
&&
widget
.
decoration
.
counterText
==
null
;
InputDecoration
_getEffectiveDecoration
()
{
final
InputDecoration
effectiveDecoration
=
(
widget
.
decoration
??
const
InputDecoration
())
.
applyDefaults
(
Theme
.
of
(
context
).
inputDecorationTheme
);
if
(!
needsCounter
)
return
widget
.
d
ecoration
;
return
effectiveD
ecoration
;
final
InputDecoration
effectiveDecoration
=
widget
?.
decoration
??
const
InputDecoration
();
final
String
counterText
=
'
${_effectiveController.value.text.runes.length}
/
${widget.maxLength}
'
;
if
(
_effectiveController
.
value
.
text
.
runes
.
length
>
widget
.
maxLength
)
{
final
ThemeData
themeData
=
Theme
.
of
(
context
);
...
...
packages/flutter/lib/src/material/text_form_field.dart
View file @
677f218e
...
...
@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import
'input_decorator.dart'
;
import
'text_field.dart'
;
import
'theme.dart'
;
/// A [FormField] that contains a [TextField].
///
...
...
@@ -71,10 +72,12 @@ class TextFormField extends FormField<String> {
validator:
validator
,
builder:
(
FormFieldState
<
String
>
field
)
{
final
_TextFormFieldState
state
=
field
;
final
InputDecoration
effectiveDecoration
=
(
decoration
??
const
InputDecoration
())
.
applyDefaults
(
Theme
.
of
(
field
.
context
).
inputDecorationTheme
);
return
new
TextField
(
controller:
state
.
_effectiveController
,
focusNode:
focusNode
,
decoration:
d
ecoration
.
copyWith
(
errorText:
field
.
errorText
),
decoration:
effectiveD
ecoration
.
copyWith
(
errorText:
field
.
errorText
),
keyboardType:
keyboardType
,
style:
style
,
textAlign:
textAlign
,
...
...
packages/flutter/lib/src/material/theme_data.dart
View file @
677f218e
...
...
@@ -10,6 +10,7 @@ import 'package:flutter/widgets.dart';
import
'colors.dart'
;
import
'ink_splash.dart'
;
import
'ink_well.dart'
show
InteractiveInkFeatureFactory
;
import
'input_decorator.dart'
;
import
'typography.dart'
;
/// Describes the contrast needs of a color.
...
...
@@ -101,6 +102,7 @@ class ThemeData {
TextTheme
textTheme
,
TextTheme
primaryTextTheme
,
TextTheme
accentTextTheme
,
InputDecorationTheme
inputDecorationTheme
,
IconThemeData
iconTheme
,
IconThemeData
primaryIconTheme
,
IconThemeData
accentIconTheme
,
...
...
@@ -135,6 +137,7 @@ class ThemeData {
indicatorColor
??=
accentColor
==
primaryColor
?
Colors
.
white
:
accentColor
;
hintColor
??=
isDark
?
const
Color
(
0x42FFFFFF
)
:
const
Color
(
0x4C000000
);
errorColor
??=
Colors
.
red
[
700
];
inputDecorationTheme
??=
const
InputDecorationTheme
();
iconTheme
??=
isDark
?
const
IconThemeData
(
color:
Colors
.
white
)
:
const
IconThemeData
(
color:
Colors
.
black
);
primaryIconTheme
??=
primaryIsDark
?
const
IconThemeData
(
color:
Colors
.
white
)
:
const
IconThemeData
(
color:
Colors
.
black
);
accentIconTheme
??=
accentIsDark
?
const
IconThemeData
(
color:
Colors
.
white
)
:
const
IconThemeData
(
color:
Colors
.
black
);
...
...
@@ -176,6 +179,7 @@ class ThemeData {
textTheme:
textTheme
,
primaryTextTheme:
primaryTextTheme
,
accentTextTheme:
accentTextTheme
,
inputDecorationTheme:
inputDecorationTheme
,
iconTheme:
iconTheme
,
primaryIconTheme:
primaryIconTheme
,
accentIconTheme:
accentIconTheme
,
...
...
@@ -217,6 +221,7 @@ class ThemeData {
@required
this
.
textTheme
,
@required
this
.
primaryTextTheme
,
@required
this
.
accentTextTheme
,
@required
this
.
inputDecorationTheme
,
@required
this
.
iconTheme
,
@required
this
.
primaryIconTheme
,
@required
this
.
accentIconTheme
,
...
...
@@ -248,6 +253,7 @@ class ThemeData {
assert
(
textTheme
!=
null
),
assert
(
primaryTextTheme
!=
null
),
assert
(
accentTextTheme
!=
null
),
assert
(
inputDecorationTheme
!=
null
),
assert
(
iconTheme
!=
null
),
assert
(
primaryIconTheme
!=
null
),
assert
(
accentIconTheme
!=
null
),
...
...
@@ -388,6 +394,12 @@ class ThemeData {
/// A text theme that contrasts with the accent color.
final
TextTheme
accentTextTheme
;
/// The default [InputDecoration] values for [InputDecorator], [TextField],
/// and [TextFormField] are based on this theme.
///
/// See [InputDecoration.applyDefaults].
final
InputDecorationTheme
inputDecorationTheme
;
/// An icon theme that contrasts with the card and canvas colors.
final
IconThemeData
iconTheme
;
...
...
@@ -431,6 +443,7 @@ class ThemeData {
TextTheme
textTheme
,
TextTheme
primaryTextTheme
,
TextTheme
accentTextTheme
,
InputDecorationTheme
inputDecorationTheme
,
IconThemeData
iconTheme
,
IconThemeData
primaryIconTheme
,
IconThemeData
accentIconTheme
,
...
...
@@ -464,6 +477,7 @@ class ThemeData {
textTheme:
textTheme
??
this
.
textTheme
,
primaryTextTheme:
primaryTextTheme
??
this
.
primaryTextTheme
,
accentTextTheme:
accentTextTheme
??
this
.
accentTextTheme
,
inputDecorationTheme:
inputDecorationTheme
??
this
.
inputDecorationTheme
,
iconTheme:
iconTheme
??
this
.
iconTheme
,
primaryIconTheme:
primaryIconTheme
??
this
.
primaryIconTheme
,
accentIconTheme:
accentIconTheme
??
this
.
accentIconTheme
,
...
...
@@ -582,6 +596,7 @@ class ThemeData {
textTheme:
TextTheme
.
lerp
(
a
.
textTheme
,
b
.
textTheme
,
t
),
primaryTextTheme:
TextTheme
.
lerp
(
a
.
primaryTextTheme
,
b
.
primaryTextTheme
,
t
),
accentTextTheme:
TextTheme
.
lerp
(
a
.
accentTextTheme
,
b
.
accentTextTheme
,
t
),
inputDecorationTheme:
t
<
0.5
?
a
.
inputDecorationTheme
:
b
.
inputDecorationTheme
,
iconTheme:
IconThemeData
.
lerp
(
a
.
iconTheme
,
b
.
iconTheme
,
t
),
primaryIconTheme:
IconThemeData
.
lerp
(
a
.
primaryIconTheme
,
b
.
primaryIconTheme
,
t
),
accentIconTheme:
IconThemeData
.
lerp
(
a
.
accentIconTheme
,
b
.
accentIconTheme
,
t
),
...
...
@@ -621,6 +636,7 @@ class ThemeData {
(
otherData
.
textTheme
==
textTheme
)
&&
(
otherData
.
primaryTextTheme
==
primaryTextTheme
)
&&
(
otherData
.
accentTextTheme
==
accentTextTheme
)
&&
(
otherData
.
inputDecorationTheme
==
inputDecorationTheme
)
&&
(
otherData
.
iconTheme
==
iconTheme
)
&&
(
otherData
.
primaryIconTheme
==
primaryIconTheme
)
&&
(
otherData
.
accentIconTheme
==
accentIconTheme
)
&&
...
...
@@ -659,6 +675,7 @@ class ThemeData {
primaryTextTheme
,
accentTextTheme
,
iconTheme
,
inputDecorationTheme
,
primaryIconTheme
,
accentIconTheme
,
platform
,
...
...
packages/flutter/test/material/input_decorator_test.dart
View file @
677f218e
...
...
@@ -8,6 +8,7 @@ import 'package:flutter_test/flutter_test.dart';
Widget
buildInputDecorator
(
{
InputDecoration
decoration:
const
InputDecoration
(),
InputDecorationTheme
inputDecorationTheme
,
TextDirection
textDirection:
TextDirection
.
ltr
,
bool
isEmpty:
false
,
bool
isFocused:
false
,
...
...
@@ -19,6 +20,12 @@ Widget buildInputDecorator({
})
{
return
new
MaterialApp
(
home:
new
Material
(
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
Theme
(
data:
Theme
.
of
(
context
).
copyWith
(
inputDecorationTheme:
inputDecorationTheme
,
),
child:
new
Align
(
alignment:
Alignment
.
topLeft
,
child:
new
Directionality
(
...
...
@@ -32,6 +39,9 @@ Widget buildInputDecorator({
),
),
),
);
},
),
),
);
}
...
...
@@ -394,14 +404,14 @@ void main() {
expect
(
getBorderWeight
(
tester
),
2.0
);
});
testWidgets
(
'InputDecorator with n
ull
border'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'InputDecorator with n
o input
border'
,
(
WidgetTester
tester
)
async
{
// Label is visible, hint is not (opacity 0.0).
await
tester
.
pumpWidget
(
buildInputDecorator
(
isEmpty:
true
,
// isFocused: false (default)
decoration:
const
InputDecoration
(
border:
null
,
border:
InputBorder
.
none
,
),
),
);
...
...
@@ -898,6 +908,131 @@ void main() {
expect
(
tester
.
getTopRight
(
find
.
text
(
'counter'
)),
const
Offset
(
788.0
,
64.0
));
});
testWidgets
(
'InputDecorationTheme outline border'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildInputDecorator
(
isEmpty:
true
,
// label appears, vertically centered
// isFocused: false (default)
inputDecorationTheme:
const
InputDecorationTheme
(
border:
const
OutlineInputBorder
(),
),
decoration:
const
InputDecoration
(
labelText:
'label'
,
),
),
);
// Overall height for this InputDecorator is 56dps. Layout is:
// 20 - top padding
// 16 - label (ahem font size 16dps)
// 20 - bottom padding
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)),
const
Size
(
800.0
,
56.0
));
expect
(
tester
.
getTopLeft
(
find
.
text
(
'label'
)).
dy
,
20.0
);
expect
(
tester
.
getBottomLeft
(
find
.
text
(
'label'
)).
dy
,
36.0
);
expect
(
getBorderBottom
(
tester
),
56.0
);
expect
(
getBorderWeight
(
tester
),
1.0
);
});
testWidgets
(
'InputDecorationTheme outline border, dense layout'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildInputDecorator
(
isEmpty:
true
,
// label appears, vertically centered
// isFocused: false (default)
inputDecorationTheme:
const
InputDecorationTheme
(
border:
const
OutlineInputBorder
(),
isDense:
true
,
),
decoration:
const
InputDecoration
(
labelText:
'label'
,
hintText:
'hint'
,
),
),
);
// Overall height for this InputDecorator is 56dps. Layout is:
// 16 - top padding
// 16 - label (ahem font size 16dps)
// 16 - bottom padding
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)),
const
Size
(
800.0
,
48.0
));
expect
(
tester
.
getTopLeft
(
find
.
text
(
'label'
)).
dy
,
16.0
);
expect
(
tester
.
getBottomLeft
(
find
.
text
(
'label'
)).
dy
,
32.0
);
expect
(
getBorderBottom
(
tester
),
48.0
);
expect
(
getBorderWeight
(
tester
),
1.0
);
});
testWidgets
(
'InputDecorationTheme style overrides'
,
(
WidgetTester
tester
)
async
{
const
TextStyle
style16
=
const
TextStyle
(
fontFamily:
'Ahem'
,
fontSize:
16.0
);
final
TextStyle
labelStyle
=
style16
.
merge
(
const
TextStyle
(
color:
Colors
.
red
));
final
TextStyle
hintStyle
=
style16
.
merge
(
const
TextStyle
(
color:
Colors
.
green
));
final
TextStyle
prefixStyle
=
style16
.
merge
(
const
TextStyle
(
color:
Colors
.
blue
));
final
TextStyle
suffixStyle
=
style16
.
merge
(
const
TextStyle
(
color:
Colors
.
purple
));
const
TextStyle
style12
=
const
TextStyle
(
fontFamily:
'Ahem'
,
fontSize:
12.0
);
final
TextStyle
helperStyle
=
style12
.
merge
(
const
TextStyle
(
color:
Colors
.
orange
));
final
TextStyle
counterStyle
=
style12
.
merge
(
const
TextStyle
(
color:
Colors
.
orange
));
// This test also verifies that the default InputDecorator provides a
// "small concession to backwards compatibility" by not padding on
// the left and right. If filled is true or an outline border is
// provided then the horizontal padding is included.
await
tester
.
pumpWidget
(
buildInputDecorator
(
isEmpty:
true
,
// label appears, vertically centered
// isFocused: false (default)
inputDecorationTheme:
new
InputDecorationTheme
(
labelStyle:
labelStyle
,
hintStyle:
hintStyle
,
prefixStyle:
prefixStyle
,
suffixStyle:
suffixStyle
,
helperStyle:
helperStyle
,
counterStyle:
counterStyle
,
// filled: false (default) - don't pad by left/right 12dps
),
decoration:
const
InputDecoration
(
labelText:
'label'
,
hintText:
'hint'
,
prefixText:
'prefix'
,
suffixText:
'suffix'
,
helperText:
'helper'
,
counterText:
'counter'
,
),
),
);
// Overall height for this InputDecorator is 76dps. Layout is:
// 12 - top padding
// 12 - floating label (ahem font size 16dps * 0.75 = 12)
// 4 - floating label / input text gap
// 16 - prefix/hint/input/suffix text (ahem font size 16dps)
// 12 - bottom padding
// 8 - below the border padding
// 12 - help/error/counter text (ahem font size 12dps)
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)),
const
Size
(
800.0
,
76.0
));
expect
(
tester
.
getTopLeft
(
find
.
text
(
'label'
)).
dy
,
20.0
);
expect
(
tester
.
getBottomLeft
(
find
.
text
(
'label'
)).
dy
,
36.0
);
expect
(
getBorderBottom
(
tester
),
56.0
);
expect
(
getBorderWeight
(
tester
),
1.0
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'helper'
)),
const
Offset
(
0.0
,
64.0
));
expect
(
tester
.
getTopRight
(
find
.
text
(
'counter'
)),
const
Offset
(
800.0
,
64.0
));
// Verify that the styles were passed along
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'prefix'
)).
style
.
color
,
prefixStyle
.
color
);
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'suffix'
)).
style
.
color
,
suffixStyle
.
color
);
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'helper'
)).
style
.
color
,
helperStyle
.
color
);
expect
(
tester
.
widget
<
Text
>(
find
.
text
(
'counter'
)).
style
.
color
,
counterStyle
.
color
);
TextStyle
getLabelStyle
()
{
return
tester
.
firstWidget
<
AnimatedDefaultTextStyle
>(
find
.
ancestor
(
of:
find
.
text
(
'label'
),
matching:
find
.
byType
(
AnimatedDefaultTextStyle
)
)
).
style
;
}
expect
(
getLabelStyle
().
color
,
labelStyle
.
color
);
});
testWidgets
(
'InputDecorator.toString()'
,
(
WidgetTester
tester
)
async
{
final
Widget
child
=
const
InputDecorator
(
key:
const
Key
(
'key'
),
...
...
@@ -910,11 +1045,11 @@ void main() {
);
expect
(
child
.
toString
(),
"InputDecorator-[<'key'>](decoration: InputDecoration(
border: UnderlineInputBorder()
), baseStyle: TextStyle(<all styles inherited>), isFocused: false, isEmpty: false)"
,
"InputDecorator-[<'key'>](decoration: InputDecoration(), baseStyle: TextStyle(<all styles inherited>), isFocused: false, isEmpty: false)"
,
);
});
testWidgets
(
'InputDecorator with
null
border and label'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'InputDecorator with
empty
border and label'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/14165
await
tester
.
pumpWidget
(
buildInputDecorator
(
...
...
@@ -922,7 +1057,7 @@ void main() {
// isFocused: false (default)
decoration:
const
InputDecoration
(
labelText:
'label'
,
border:
null
,
border:
InputBorder
.
none
,
),
),
);
...
...
@@ -932,4 +1067,84 @@ void main() {
expect
(
tester
.
getTopLeft
(
find
.
text
(
'label'
)).
dy
,
12.0
);
expect
(
tester
.
getBottomLeft
(
find
.
text
(
'label'
)).
dy
,
24.0
);
});
testWidgets
(
'InputDecorationTheme.inputDecoration'
,
(
WidgetTester
tester
)
async
{
const
TextStyle
themeStyle
=
const
TextStyle
(
color:
Colors
.
green
);
const
TextStyle
decorationStyle
=
const
TextStyle
(
color:
Colors
.
blue
);
// InputDecorationTheme arguments define InputDecoration properties.
InputDecoration
decoration
=
const
InputDecoration
().
applyDefaults
(
const
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
,
border:
InputBorder
.
none
,
)
);
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
);
// InputDecoration (baseDecoration) defines InputDecoration properties
decoration
=
const
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:
const
OutlineInputBorder
(),
).
applyDefaults
(
const
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
,
border:
InputBorder
.
none
,
),
);
expect
(
decoration
.
labelStyle
,
decorationStyle
);
expect
(
decoration
.
helperStyle
,
decorationStyle
);
expect
(
decoration
.
hintStyle
,
decorationStyle
);
expect
(
decoration
.
errorStyle
,
decorationStyle
);
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
,
const
OutlineInputBorder
());
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment