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
f21497ee
Unverified
Commit
f21497ee
authored
Mar 09, 2019
by
Todd Volkert
Committed by
GitHub
Mar 09, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Fix TextField height issues (#27205)" (#29093)
This reverts commit
9e9f48da
.
parent
a0d1f93b
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
170 additions
and
774 deletions
+170
-774
input_decorator.dart
packages/flutter/lib/src/material/input_decorator.dart
+84
-187
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+0
-23
box.dart
packages/flutter/lib/src/rendering/box.dart
+2
-2
editable.dart
packages/flutter/lib/src/rendering/editable.dart
+1
-60
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+4
-94
input_decorator_test.dart
packages/flutter/test/material/input_decorator_test.dart
+7
-100
text_field_focus_test.dart
packages/flutter/test/material/text_field_focus_test.dart
+41
-41
text_field_test.dart
packages/flutter/test/material/text_field_test.dart
+31
-266
editable_test.dart
packages/flutter/test/rendering/editable_test.dart
+0
-1
No files found.
packages/flutter/lib/src/material/input_decorator.dart
View file @
f21497ee
...
...
@@ -552,16 +552,13 @@ class _RenderDecoration extends RenderBox {
@required
TextDirection
textDirection
,
@required
TextBaseline
textBaseline
,
@required
bool
isFocused
,
@required
bool
expands
,
})
:
assert
(
decoration
!=
null
),
assert
(
textDirection
!=
null
),
assert
(
textBaseline
!=
null
),
assert
(
expands
!=
null
),
_decoration
=
decoration
,
_textDirection
=
textDirection
,
_textBaseline
=
textBaseline
,
_isFocused
=
isFocused
,
_expands
=
expands
;
_isFocused
=
isFocused
;
final
Map
<
_DecorationSlot
,
RenderBox
>
slotToChild
=
<
_DecorationSlot
,
RenderBox
>{};
final
Map
<
RenderBox
,
_DecorationSlot
>
childToSlot
=
<
RenderBox
,
_DecorationSlot
>{};
...
...
@@ -712,16 +709,6 @@ class _RenderDecoration extends RenderBox {
markNeedsSemanticsUpdate
();
}
bool
get
expands
=>
_expands
;
bool
_expands
=
false
;
set
expands
(
bool
value
)
{
assert
(
value
!=
null
);
if
(
_expands
==
value
)
return
;
_expands
=
value
;
markNeedsLayout
();
}
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
...
...
@@ -817,31 +804,34 @@ class _RenderDecoration extends RenderBox {
EdgeInsets
get
contentPadding
=>
decoration
.
contentPadding
;
// Lay out the given box if needed, and return its baseline
double
_layoutLineBox
(
RenderBox
box
,
BoxConstraints
constraints
)
{
if
(
box
==
null
)
{
return
0.0
;
}
box
.
layout
(
constraints
,
parentUsesSize:
true
);
final
double
baseline
=
box
.
getDistanceToBaseline
(
textBaseline
);
assert
(
baseline
!=
null
&&
baseline
>=
0.0
);
return
baseline
;
}
// Returns a value used by performLayout to position all of the renderers.
// This method applies layout to all of the renderers except the container.
// For convenience, the container is laid out in performLayout().
// Returns a value used by performLayout to position all
// of the renderers. This method applies layout to all of the renderers
// except the container. For convenience, the container is laid out
// in performLayout().
_RenderDecorationLayout
_layout
(
BoxConstraints
layoutConstraints
)
{
// Margin on each side of subtext (counter and helperError)
final
Map
<
RenderBox
,
double
>
boxToBaseline
=
<
RenderBox
,
double
>{};
final
BoxConstraints
boxConstraints
=
layoutConstraints
.
loosen
();
BoxConstraints
boxConstraints
=
layoutConstraints
.
loosen
();
double
aboveBaseline
=
0.0
;
double
belowBaseline
=
0.0
;
void
layoutLineBox
(
RenderBox
box
)
{
if
(
box
==
null
)
return
;
box
.
layout
(
boxConstraints
,
parentUsesSize:
true
);
final
double
baseline
=
box
.
getDistanceToBaseline
(
textBaseline
);
assert
(
baseline
!=
null
&&
baseline
>=
0.0
);
boxToBaseline
[
box
]
=
baseline
;
aboveBaseline
=
math
.
max
(
baseline
,
aboveBaseline
);
belowBaseline
=
math
.
max
(
box
.
size
.
height
-
baseline
,
belowBaseline
);
}
layoutLineBox
(
prefix
);
layoutLineBox
(
suffix
);
// Layout all the widgets used by InputDecorator
boxToBaseline
[
prefix
]
=
_layoutLineBox
(
prefix
,
boxConstraints
);
boxToBaseline
[
suffix
]
=
_layoutLineBox
(
suffix
,
boxConstraints
);
boxToBaseline
[
icon
]
=
_layoutLineBox
(
icon
,
boxConstraints
);
boxToBaseline
[
prefixIcon
]
=
_layoutLineBox
(
prefixIcon
,
boxConstraints
);
boxToBaseline
[
suffixIcon
]
=
_layoutLineBox
(
suffixIcon
,
boxConstraints
);
if
(
icon
!=
null
)
icon
.
layout
(
boxConstraints
,
parentUsesSize:
true
);
if
(
prefixIcon
!=
null
)
prefixIcon
.
layout
(
boxConstraints
,
parentUsesSize:
true
);
if
(
suffixIcon
!=
null
)
suffixIcon
.
layout
(
boxConstraints
,
parentUsesSize:
true
);
final
double
inputWidth
=
math
.
max
(
0.0
,
constraints
.
maxWidth
-
(
_boxSize
(
icon
).
width
...
...
@@ -851,144 +841,72 @@ class _RenderDecoration extends RenderBox {
+
_boxSize
(
suffix
).
width
+
_boxSize
(
suffixIcon
).
width
+
contentPadding
.
right
));
boxToBaseline
[
label
]
=
_layoutLineBox
(
label
,
boxConstraints
.
copyWith
(
maxWidth:
inputWidth
),
);
boxToBaseline
[
hint
]
=
_layoutLineBox
(
hint
,
boxConstraints
.
copyWith
(
minWidth:
inputWidth
,
maxWidth:
inputWidth
),
);
boxToBaseline
[
counter
]
=
_layoutLineBox
(
counter
,
boxConstraints
);
// The helper or error text can occupy the full width less the space
// occupied by the icon and counter.
boxToBaseline
[
helperError
]
=
_layoutLineBox
(
helperError
,
boxConstraints
.
copyWith
(
boxConstraints
=
boxConstraints
.
copyWith
(
maxWidth:
inputWidth
);
if
(
label
!=
null
)
{
if
(
decoration
.
alignLabelWithHint
)
{
// The label is aligned with the hint, at the baseline
layoutLineBox
(
label
);
}
else
{
// The label is centered, not baseline aligned
label
.
layout
(
boxConstraints
,
parentUsesSize:
true
);
}
}
boxConstraints
=
boxConstraints
.
copyWith
(
minWidth:
inputWidth
);
layoutLineBox
(
hint
);
layoutLineBox
(
input
);
double
inputBaseline
=
contentPadding
.
top
+
aboveBaseline
;
double
containerHeight
=
contentPadding
.
top
+
aboveBaseline
+
belowBaseline
+
contentPadding
.
bottom
;
if
(
label
!=
null
)
{
// floatingLabelHeight includes the vertical gap between the inline
// elements and the floating label.
containerHeight
+=
decoration
.
floatingLabelHeight
;
inputBaseline
+=
decoration
.
floatingLabelHeight
;
}
containerHeight
=
math
.
max
(
containerHeight
,
math
.
max
(
_boxSize
(
suffixIcon
).
height
,
_boxSize
(
prefixIcon
).
height
));
// Inline text within an outline border is centered within the container
// less 2.0 dps at the top to account for the vertical space occupied
// by the floating label.
final
double
outlineBaseline
=
aboveBaseline
+
(
containerHeight
-
(
2.0
+
aboveBaseline
+
belowBaseline
))
/
2.0
;
double
subtextBaseline
=
0.0
;
double
subtextHeight
=
0.0
;
if
(
helperError
!=
null
||
counter
!=
null
)
{
boxConstraints
=
layoutConstraints
.
loosen
();
aboveBaseline
=
0.0
;
belowBaseline
=
0.0
;
layoutLineBox
(
counter
);
// The helper or error text can occupy the full width less the space
// occupied by the icon and counter.
boxConstraints
=
boxConstraints
.
copyWith
(
maxWidth:
math
.
max
(
0.0
,
boxConstraints
.
maxWidth
-
_boxSize
(
icon
).
width
-
_boxSize
(
counter
).
width
-
contentPadding
.
horizontal
,
),
),
);
// The height of the input needs to accommodate label above and counter and
// helperError below, when they exist.
const
double
subtextGap
=
8.0
;
final
double
labelHeight
=
label
==
null
?
0
:
decoration
.
floatingLabelHeight
;
final
double
topHeight
=
decoration
.
border
.
isOutline
?
math
.
max
(
labelHeight
-
boxToBaseline
[
label
],
0
)
:
labelHeight
;
final
double
counterHeight
=
counter
==
null
?
0
:
boxToBaseline
[
counter
]
+
subtextGap
*
2
;
final
_HelperError
helperErrorWidget
=
decoration
.
helperError
;
final
double
helperErrorHeight
=
helperErrorWidget
.
helperText
==
null
?
0
:
helperError
.
size
.
height
+
subtextGap
*
2
;
final
double
bottomHeight
=
math
.
max
(
counterHeight
,
helperErrorHeight
,
);
boxToBaseline
[
input
]
=
_layoutLineBox
(
input
,
boxConstraints
.
deflate
(
EdgeInsets
.
only
(
top:
contentPadding
.
top
+
topHeight
,
bottom:
contentPadding
.
bottom
+
bottomHeight
,
)).
copyWith
(
minWidth:
inputWidth
,
maxWidth:
inputWidth
,
),
);
// The field can be occupied by a hint or by the input itself
final
double
hintHeight
=
hint
==
null
?
0
:
hint
.
size
.
height
;
final
double
inputDirectHeight
=
input
==
null
?
0
:
input
.
size
.
height
;
final
double
inputHeight
=
math
.
max
(
hintHeight
,
inputDirectHeight
);
final
double
inputInternalBaseline
=
math
.
max
(
boxToBaseline
[
input
],
boxToBaseline
[
hint
],
);
// Calculate the amount that prefix/suffix affects height above and below
// the input.
final
double
prefixHeight
=
prefix
==
null
?
0
:
prefix
.
size
.
height
;
final
double
suffixHeight
=
suffix
==
null
?
0
:
suffix
.
size
.
height
;
final
double
fixHeight
=
math
.
max
(
boxToBaseline
[
prefix
],
boxToBaseline
[
suffix
],
);
final
double
fixAboveInput
=
math
.
max
(
0
,
fixHeight
-
inputInternalBaseline
);
final
double
fixBelowBaseline
=
math
.
max
(
prefixHeight
-
boxToBaseline
[
prefix
],
suffixHeight
-
boxToBaseline
[
suffix
],
);
final
double
fixBelowInput
=
math
.
max
(
0
,
fixBelowBaseline
-
(
inputHeight
-
inputInternalBaseline
),
);
);
layoutLineBox
(
helperError
);
// Calculate the height of the input text container.
final
double
prefixIconHeight
=
prefixIcon
==
null
?
0
:
prefixIcon
.
size
.
height
;
final
double
suffixIconHeight
=
suffixIcon
==
null
?
0
:
suffixIcon
.
size
.
height
;
final
double
fixIconHeight
=
math
.
max
(
prefixIconHeight
,
suffixIconHeight
);
final
double
contentHeight
=
math
.
max
(
fixIconHeight
,
topHeight
+
contentPadding
.
top
+
fixAboveInput
+
inputHeight
+
fixBelowInput
+
contentPadding
.
bottom
,
);
final
double
maxContainerHeight
=
boxConstraints
.
maxHeight
-
bottomHeight
;
final
double
containerHeight
=
expands
?
maxContainerHeight
:
math
.
min
(
contentHeight
,
maxContainerHeight
);
// Always position the prefix/suffix in the same place (baseline).
final
double
overflow
=
math
.
max
(
0
,
contentHeight
-
maxContainerHeight
);
final
double
baselineAdjustment
=
fixAboveInput
-
overflow
;
// The baselines that will be used to draw the actual input text content.
final
double
inputBaseline
=
contentPadding
.
top
+
topHeight
+
inputInternalBaseline
+
baselineAdjustment
;
// The text in the input when an outline border is present is centered
// within the container less 2.0 dps at the top to account for the vertical
// space occupied by the floating label.
final
double
outlineBaseline
=
inputInternalBaseline
+
baselineAdjustment
/
2
+
(
containerHeight
-
(
2.0
+
inputHeight
))
/
2.0
;
// Find the positions of the text below the input when it exists.
double
subtextCounterBaseline
=
0
;
double
subtextHelperBaseline
=
0
;
double
subtextCounterHeight
=
0
;
double
subtextHelperHeight
=
0
;
if
(
counter
!=
null
)
{
subtextCounterBaseline
=
containerHeight
+
subtextGap
+
boxToBaseline
[
counter
];
subtextCounterHeight
=
counter
.
size
.
height
+
subtextGap
;
}
if
(
helperErrorWidget
.
helperText
!=
null
)
{
subtextHelperBaseline
=
containerHeight
+
subtextGap
+
boxToBaseline
[
helperError
];
subtextHelperHeight
=
helperError
.
size
.
height
+
subtextGap
;
if
(
aboveBaseline
+
belowBaseline
>
0.0
)
{
const
double
subtextGap
=
8.0
;
subtextBaseline
=
containerHeight
+
subtextGap
+
aboveBaseline
;
subtextHeight
=
subtextGap
+
aboveBaseline
+
belowBaseline
;
}
}
final
double
subtextBaseline
=
math
.
max
(
subtextCounterBaseline
,
subtextHelperBaseline
,
);
final
double
subtextHeight
=
math
.
max
(
subtextCounterHeight
,
subtextHelperHeight
,
);
return
_RenderDecorationLayout
(
boxToBaseline:
boxToBaseline
,
...
...
@@ -1452,18 +1370,15 @@ class _Decorator extends RenderObjectWidget {
@required
this
.
textDirection
,
@required
this
.
textBaseline
,
@required
this
.
isFocused
,
@required
this
.
expands
,
})
:
assert
(
decoration
!=
null
),
assert
(
textDirection
!=
null
),
assert
(
textBaseline
!=
null
),
assert
(
expands
!=
null
),
super
(
key:
key
);
final
_Decoration
decoration
;
final
TextDirection
textDirection
;
final
TextBaseline
textBaseline
;
final
bool
isFocused
;
final
bool
expands
;
@override
_RenderDecorationElement
createElement
()
=>
_RenderDecorationElement
(
this
);
...
...
@@ -1475,7 +1390,6 @@ class _Decorator extends RenderObjectWidget {
textDirection:
textDirection
,
textBaseline:
textBaseline
,
isFocused:
isFocused
,
expands:
expands
,
);
}
...
...
@@ -1485,7 +1399,6 @@ class _Decorator extends RenderObjectWidget {
..
decoration
=
decoration
..
textDirection
=
textDirection
..
textBaseline
=
textBaseline
..
expands
=
expands
..
isFocused
=
isFocused
;
}
}
...
...
@@ -1548,7 +1461,6 @@ class InputDecorator extends StatefulWidget {
this
.
baseStyle
,
this
.
textAlign
,
this
.
isFocused
=
false
,
this
.
expands
=
false
,
this
.
isEmpty
=
false
,
this
.
child
,
})
:
assert
(
isFocused
!=
null
),
...
...
@@ -1583,19 +1495,6 @@ class InputDecorator extends StatefulWidget {
/// Defaults to false.
final
bool
isFocused
;
/// If true, the height of the input field will be as large as possible.
///
/// If wrapped in a widget that constrains its child's height, like Expanded
/// or SizedBox, the input field will only be affected if [expands] is set to
/// true.
///
/// See [TextField.minLines] and [TextField.maxLines] for related ways to
/// affect the height of an input. When [expands] is true, both must be null
/// in order to avoid ambiguity in determining the height.
///
/// Defaults to false.
final
bool
expands
;
/// Whether the input field is empty.
///
/// Determines the position of the label text and whether to display the hint
...
...
@@ -1634,7 +1533,6 @@ class InputDecorator extends StatefulWidget {
properties
.
add
(
DiagnosticsProperty
<
InputDecoration
>(
'decoration'
,
decoration
));
properties
.
add
(
DiagnosticsProperty
<
TextStyle
>(
'baseStyle'
,
baseStyle
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'isFocused'
,
isFocused
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'expands'
,
expands
,
defaultValue:
false
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'isEmpty'
,
isEmpty
));
}
}
...
...
@@ -2030,7 +1928,6 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
textDirection:
textDirection
,
textBaseline:
textBaseline
,
isFocused:
isFocused
,
expands:
widget
.
expands
,
);
}
}
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
f21497ee
...
...
@@ -147,8 +147,6 @@ class TextField extends StatefulWidget {
this
.
obscureText
=
false
,
this
.
autocorrect
=
true
,
this
.
maxLines
=
1
,
this
.
minLines
,
this
.
expands
=
false
,
this
.
maxLength
,
this
.
maxLengthEnforced
=
true
,
this
.
onChanged
,
...
...
@@ -173,16 +171,6 @@ class TextField extends StatefulWidget {
assert
(
scrollPadding
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
maxLines
==
null
||
maxLines
>
0
),
assert
(
minLines
==
null
||
minLines
>
0
),
assert
(
(
maxLines
==
null
)
||
(
minLines
==
null
)
||
(
maxLines
>=
minLines
),
'minLines can
\'
t be greater than maxLines'
,
),
assert
(
expands
!=
null
),
assert
(
!
expands
||
(
maxLines
==
null
&&
minLines
==
null
),
'minLines and maxLines must be null when expands is true.'
,
),
assert
(
maxLength
==
null
||
maxLength
==
TextField
.
noMaxLength
||
maxLength
>
0
),
keyboardType
=
keyboardType
??
(
maxLines
==
1
?
TextInputType
.
text
:
TextInputType
.
multiline
),
super
(
key:
key
);
...
...
@@ -281,12 +269,6 @@ class TextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.maxLines}
final
int
maxLines
;
/// {@macro flutter.widgets.editableText.minLines}
final
int
minLines
;
/// {@macro flutter.widgets.editableText.expands}
final
bool
expands
;
/// If [maxLength] is set to this value, only the "current input length"
/// part of the character counter is shown.
static
const
int
noMaxLength
=
-
1
;
...
...
@@ -475,8 +457,6 @@ class TextField extends StatefulWidget {
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'obscureText'
,
obscureText
,
defaultValue:
false
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'autocorrect'
,
autocorrect
,
defaultValue:
true
));
properties
.
add
(
IntProperty
(
'maxLines'
,
maxLines
,
defaultValue:
1
));
properties
.
add
(
IntProperty
(
'minLines'
,
minLines
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'expands'
,
expands
,
defaultValue:
false
));
properties
.
add
(
IntProperty
(
'maxLength'
,
maxLength
,
defaultValue:
null
));
properties
.
add
(
FlagProperty
(
'maxLengthEnforced'
,
value:
maxLengthEnforced
,
defaultValue:
true
,
ifFalse:
'maxLength not enforced'
));
properties
.
add
(
EnumProperty
<
TextInputAction
>(
'textInputAction'
,
textInputAction
,
defaultValue:
null
));
...
...
@@ -871,8 +851,6 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
selectionColor:
themeData
.
textSelectionColor
,
selectionControls:
widget
.
selectionEnabled
?
textSelectionControls
:
null
,
onChanged:
widget
.
onChanged
,
...
...
@@ -905,7 +883,6 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
textAlign:
widget
.
textAlign
,
isFocused:
focusNode
.
hasFocus
,
isEmpty:
controller
.
value
.
text
.
isEmpty
,
expands:
widget
.
expands
,
child:
child
,
);
},
...
...
packages/flutter/lib/src/rendering/box.dart
View file @
f21497ee
...
...
@@ -1801,9 +1801,9 @@ abstract class RenderBox extends RenderObject {
testIntrinsicsForValues
(
getMinIntrinsicWidth
,
getMaxIntrinsicWidth
,
'Width'
,
double
.
infinity
);
testIntrinsicsForValues
(
getMinIntrinsicHeight
,
getMaxIntrinsicHeight
,
'Height'
,
double
.
infinity
);
if
(
constraints
.
hasBoundedWidth
)
testIntrinsicsForValues
(
getMinIntrinsicWidth
,
getMaxIntrinsicWidth
,
'Width'
,
constraints
.
max
Height
);
testIntrinsicsForValues
(
getMinIntrinsicWidth
,
getMaxIntrinsicWidth
,
'Width'
,
constraints
.
max
Width
);
if
(
constraints
.
hasBoundedHeight
)
testIntrinsicsForValues
(
getMinIntrinsicHeight
,
getMaxIntrinsicHeight
,
'Height'
,
constraints
.
max
Width
);
testIntrinsicsForValues
(
getMinIntrinsicHeight
,
getMaxIntrinsicHeight
,
'Height'
,
constraints
.
max
Height
);
// TODO(ianh): Test that values are internally consistent in more ways than the above.
...
...
packages/flutter/lib/src/rendering/editable.dart
View file @
f21497ee
...
...
@@ -144,8 +144,6 @@ class RenderEditable extends RenderBox {
ValueNotifier
<
bool
>
showCursor
,
bool
hasFocus
,
int
maxLines
=
1
,
int
minLines
,
bool
expands
=
false
,
StrutStyle
strutStyle
,
Color
selectionColor
,
double
textScaleFactor
=
1.0
,
...
...
@@ -167,16 +165,6 @@ class RenderEditable extends RenderBox {
})
:
assert
(
textAlign
!=
null
),
assert
(
textDirection
!=
null
,
'RenderEditable created without a textDirection.'
),
assert
(
maxLines
==
null
||
maxLines
>
0
),
assert
(
minLines
==
null
||
minLines
>
0
),
assert
(
(
maxLines
==
null
)
||
(
minLines
==
null
)
||
(
maxLines
>=
minLines
),
'minLines can
\'
t be greater than maxLines'
,
),
assert
(
expands
!=
null
),
assert
(
!
expands
||
(
maxLines
==
null
&&
minLines
==
null
),
'minLines and maxLines must be null when expands is true.'
,
),
assert
(
textScaleFactor
!=
null
),
assert
(
offset
!=
null
),
assert
(
ignorePointer
!=
null
),
...
...
@@ -198,8 +186,6 @@ class RenderEditable extends RenderBox {
_showCursor
=
showCursor
??
ValueNotifier
<
bool
>(
false
),
_hasFocus
=
hasFocus
??
false
,
_maxLines
=
maxLines
,
_minLines
=
minLines
,
_expands
=
expands
,
_selectionColor
=
selectionColor
,
_selection
=
selection
,
_offset
=
offset
,
...
...
@@ -705,29 +691,6 @@ class RenderEditable extends RenderBox {
markNeedsTextLayout
();
}
/// {@macro flutter.widgets.editableText.minLines}
int
get
minLines
=>
_minLines
;
int
_minLines
;
/// The value may be null. If it is not null, then it must be greater than zero.
set
minLines
(
int
value
)
{
assert
(
value
==
null
||
value
>
0
);
if
(
minLines
==
value
)
return
;
_minLines
=
value
;
markNeedsTextLayout
();
}
/// {@macro flutter.widgets.editableText.expands}
bool
get
expands
=>
_expands
;
bool
_expands
;
set
expands
(
bool
value
)
{
assert
(
value
!=
null
);
if
(
expands
==
value
)
return
;
_expands
=
value
;
markNeedsTextLayout
();
}
/// The color to use when painting the selection.
Color
get
selectionColor
=>
_selectionColor
;
Color
_selectionColor
;
...
...
@@ -1187,28 +1150,8 @@ class RenderEditable extends RenderBox {
double
get
preferredLineHeight
=>
_textPainter
.
preferredLineHeight
;
double
_preferredHeight
(
double
width
)
{
// Lock height to maxLines if needed
final
bool
lockedMax
=
maxLines
!=
null
&&
minLines
==
null
;
final
bool
lockedBoth
=
minLines
!=
null
&&
minLines
==
maxLines
;
final
bool
singleLine
=
maxLines
==
1
;
if
(
singleLine
||
lockedMax
||
lockedBoth
)
{
if
(
maxLines
!=
null
)
return
preferredLineHeight
*
maxLines
;
}
// Clamp height to minLines or maxLines if needed
final
bool
minLimited
=
minLines
!=
null
&&
minLines
>
1
;
final
bool
maxLimited
=
maxLines
!=
null
;
if
(
minLimited
||
maxLimited
)
{
_layoutText
(
width
);
if
(
minLimited
&&
_textPainter
.
height
<
preferredLineHeight
*
minLines
)
{
return
preferredLineHeight
*
minLines
;
}
if
(
maxLimited
&&
_textPainter
.
height
>
preferredLineHeight
*
maxLines
)
{
return
preferredLineHeight
*
maxLines
;
}
}
// Set the height based on the content
if
(
width
==
double
.
infinity
)
{
final
String
text
=
_textPainter
.
text
.
toPlainText
();
int
lines
=
1
;
...
...
@@ -1670,8 +1613,6 @@ class RenderEditable extends RenderBox {
properties
.
add
(
DiagnosticsProperty
<
Color
>(
'cursorColor'
,
cursorColor
));
properties
.
add
(
DiagnosticsProperty
<
ValueNotifier
<
bool
>>(
'showCursor'
,
showCursor
));
properties
.
add
(
IntProperty
(
'maxLines'
,
maxLines
));
properties
.
add
(
IntProperty
(
'minLines'
,
minLines
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'expands'
,
expands
,
defaultValue:
false
));
properties
.
add
(
DiagnosticsProperty
<
Color
>(
'selectionColor'
,
selectionColor
));
properties
.
add
(
DoubleProperty
(
'textScaleFactor'
,
textScaleFactor
));
properties
.
add
(
DiagnosticsProperty
<
Locale
>(
'locale'
,
locale
,
defaultValue:
null
));
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
f21497ee
...
...
@@ -278,8 +278,6 @@ class EditableText extends StatefulWidget {
this
.
locale
,
this
.
textScaleFactor
,
this
.
maxLines
=
1
,
this
.
minLines
,
this
.
expands
=
false
,
this
.
autofocus
=
false
,
this
.
selectionColor
,
this
.
selectionControls
,
...
...
@@ -312,16 +310,6 @@ class EditableText extends StatefulWidget {
assert
(
backgroundCursorColor
!=
null
),
assert
(
textAlign
!=
null
),
assert
(
maxLines
==
null
||
maxLines
>
0
),
assert
(
minLines
==
null
||
minLines
>
0
),
assert
(
(
maxLines
==
null
)
||
(
minLines
==
null
)
||
(
maxLines
>=
minLines
),
'minLines can
\'
t be greater than maxLines'
,
),
assert
(
expands
!=
null
),
assert
(
!
expands
||
(
maxLines
==
null
&&
minLines
==
null
),
'minLines and maxLines must be null when expands is true.'
,
),
assert
(
autofocus
!=
null
),
assert
(
rendererIgnoresPointer
!=
null
),
assert
(
scrollPadding
!=
null
),
...
...
@@ -477,78 +465,12 @@ class EditableText extends StatefulWidget {
/// container will start with enough vertical space for one line and
/// automatically grow to accommodate additional lines as they are entered.
///
/// If this is not null, the value must be greater than zero, and it will lock
/// the input to the given number of lines and take up enough horizontal space
/// to accommodate that number of lines. Setting [minLines] as well allows the
/// input to grow between the indicated range.
///
/// The full set of behaviors possible with [minLines] and [maxLines] are as
/// follows. These examples apply equally to `TextField`, `TextFormField`, and
/// `EditableText`.
///
/// Input that occupies a single line and scrolls horizontally as needed.
/// ```dart
/// TextField()
/// ```
///
/// Input whose height grows from one line up to as many lines as needed for
/// the text that was entered. If a height limit is imposed by its parent, it
/// will scroll vertically when its height reaches that limit.
/// ```dart
/// TextField(maxLines: null)
/// ```
///
/// The input's height is large enough for the given number of lines. If
/// additional lines are entered the input scrolls vertically.
/// ```dart
/// TextField(maxLines: 2)
/// ```
///
/// Input whose height grows with content between a min and max. An infinite
/// max is possible with `maxLines: null`.
/// ```dart
/// TextField(minLines: 2, maxLines: 4)
/// ```
/// If it is not null, the value must be greater than zero. If it is greater
/// than 1, it will take up enough horizontal space to accommodate that number
/// of lines.
/// {@endtemplate}
final
int
maxLines
;
/// {@template flutter.widgets.editableText.minLines}
/// The minimum number of lines to occupy when the content spans fewer lines.
/// When [maxLines] is set as well, the height will grow between the indicated
/// range of lines. When [maxLines] is null, it will grow as high as needed,
/// starting from [minLines].
///
/// See the examples in [maxLines] for the complete picture of how [maxLines]
/// and [minLines] interact to produce various behaviors.
///
/// Defaults to null.
/// {@endtemplate}
final
int
minLines
;
/// {@template flutter.widgets.editableText.expands}
/// Whether this widget's height will be sized to fill its parent.
///
/// If set to true and wrapped in a parent widget like [Expanded] or
/// [SizedBox], the input will expand to fill the parent.
///
/// [maxLines] and [minLines] must both be null when this is set to true,
/// otherwise an error is thrown.
///
/// Defaults to false.
///
/// See the examples in [maxLines] for the complete picture of how [maxLines],
/// [minLines], and [expands] interact to produce various behaviors.
///
/// Input that matches the height of its parent
/// ```dart
/// Expanded(
/// child: TextField(maxLines: null, expands: true),
/// )
/// ```
/// {@endtemplate}
final
bool
expands
;
/// {@template flutter.widgets.editableText.autofocus}
/// Whether this text field should focus itself if nothing else is already
/// focused.
...
...
@@ -754,8 +676,6 @@ class EditableText extends StatefulWidget {
properties
.
add
(
DiagnosticsProperty
<
Locale
>(
'locale'
,
locale
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'textScaleFactor'
,
textScaleFactor
,
defaultValue:
null
));
properties
.
add
(
IntProperty
(
'maxLines'
,
maxLines
,
defaultValue:
1
));
properties
.
add
(
IntProperty
(
'minLines'
,
minLines
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'expands'
,
expands
,
defaultValue:
false
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'autofocus'
,
autofocus
,
defaultValue:
false
));
properties
.
add
(
DiagnosticsProperty
<
TextInputType
>(
'keyboardType'
,
keyboardType
,
defaultValue:
null
));
}
...
...
@@ -875,7 +795,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
// If this is a multiline EditableText, do nothing for a "newline"
// action; The newline is already inserted. Otherwise, finalize
// editing.
if
(
!
_isMultiline
)
if
(
widget
.
maxLines
==
1
)
_finalizeEditing
(
true
);
break
;
case
TextInputAction
.
done
:
...
...
@@ -1413,8 +1333,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
:
_cursorVisibilityNotifier
,
hasFocus:
_hasFocus
,
maxLines:
widget
.
maxLines
,
minLines:
widget
.
minLines
,
expands:
widget
.
expands
,
strutStyle:
widget
.
strutStyle
,
selectionColor:
widget
.
selectionColor
,
textScaleFactor:
widget
.
textScaleFactor
??
MediaQuery
.
textScaleFactorOf
(
context
),
...
...
@@ -1485,8 +1403,6 @@ class _Editable extends LeafRenderObjectWidget {
this
.
showCursor
,
this
.
hasFocus
,
this
.
maxLines
,
this
.
minLines
,
this
.
expands
,
this
.
strutStyle
,
this
.
selectionColor
,
this
.
textScaleFactor
,
...
...
@@ -1517,8 +1433,6 @@ class _Editable extends LeafRenderObjectWidget {
final
ValueNotifier
<
bool
>
showCursor
;
final
bool
hasFocus
;
final
int
maxLines
;
final
int
minLines
;
final
bool
expands
;
final
StrutStyle
strutStyle
;
final
Color
selectionColor
;
final
double
textScaleFactor
;
...
...
@@ -1548,8 +1462,6 @@ class _Editable extends LeafRenderObjectWidget {
showCursor:
showCursor
,
hasFocus:
hasFocus
,
maxLines:
maxLines
,
minLines:
minLines
,
expands:
expands
,
strutStyle:
strutStyle
,
selectionColor:
selectionColor
,
textScaleFactor:
textScaleFactor
,
...
...
@@ -1580,8 +1492,6 @@ class _Editable extends LeafRenderObjectWidget {
..
showCursor
=
showCursor
..
hasFocus
=
hasFocus
..
maxLines
=
maxLines
..
minLines
=
minLines
..
expands
=
expands
..
strutStyle
=
strutStyle
..
selectionColor
=
selectionColor
..
textScaleFactor
=
textScaleFactor
...
...
packages/flutter/test/material/input_decorator_test.dart
View file @
f21497ee
...
...
@@ -993,100 +993,6 @@ void main() {
expect
(
tester
.
getTopRight
(
find
.
text
(
'text'
)).
dx
,
lessThanOrEqualTo
(
tester
.
getTopRight
(
find
.
byKey
(
sKey
)).
dx
));
});
testWidgets
(
'InputDecorator tall prefix'
,
(
WidgetTester
tester
)
async
{
const
Key
pKey
=
Key
(
'p'
);
await
tester
.
pumpWidget
(
buildInputDecorator
(
// isEmpty: false (default)
// isFocused: false (default)
decoration:
InputDecoration
(
prefix:
Container
(
key:
pKey
,
height:
100
,
width:
10
,
),
filled:
true
,
),
// Set the fontSize so that everything works out to whole numbers.
child:
const
Text
(
'text'
,
style:
TextStyle
(
fontFamily:
'Ahem'
,
fontSize:
20.0
),
),
),
);
// Overall height for this InputDecorator is ~127.2dps because
// the prefix is 100dps tall, but it aligns with the input's baseline,
// overlapping the input a bit.
// 12 - top padding
// 100 - total height of prefix
// -16 - input prefix overlap (distance input top to baseline, not exact)
// 20 - input text (ahem font size 16dps)
// 0 - bottom prefix/suffix padding
// 12 - bottom padding
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)).
width
,
800.0
);
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)).
height
,
closeTo
(
128.0
,
.
0001
));
expect
(
tester
.
getSize
(
find
.
text
(
'text'
)).
height
,
20.0
);
expect
(
tester
.
getSize
(
find
.
byKey
(
pKey
)).
height
,
100.0
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'text'
)).
dy
,
closeTo
(
96
,
.
0001
));
// 12 + 100 - 16
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
pKey
)).
dy
,
12.0
);
// layout is a row: [prefix text suffix]
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
pKey
)).
dx
,
12.0
);
expect
(
tester
.
getTopRight
(
find
.
byKey
(
pKey
)).
dx
,
tester
.
getTopLeft
(
find
.
text
(
'text'
)).
dx
);
});
testWidgets
(
'InputDecorator tall prefix with border'
,
(
WidgetTester
tester
)
async
{
const
Key
pKey
=
Key
(
'p'
);
await
tester
.
pumpWidget
(
buildInputDecorator
(
// isEmpty: false (default)
// isFocused: false (default)
decoration:
InputDecoration
(
border:
const
OutlineInputBorder
(),
prefix:
Container
(
key:
pKey
,
height:
100
,
width:
10
,
),
filled:
true
,
),
// Set the fontSize so that everything works out to whole numbers.
child:
const
Text
(
'text'
,
style:
TextStyle
(
fontFamily:
'Ahem'
,
fontSize:
20.0
),
),
),
);
// Overall height for this InputDecorator is ~127.2dps because
// the prefix is 100dps tall, but it aligns with the input's baseline,
// overlapping the input a bit.
// 24 - top padding
// 100 - total height of prefix
// -16 - input prefix overlap (distance input top to baseline, not exact)
// 20 - input text (ahem font size 16dps)
// 0 - bottom prefix/suffix padding
// 16 - bottom padding
// When a border is present, the input text and prefix/suffix are centered
// within the input. Here, that will be content of height 106, including 2
// extra pixels of space, centered within an input of height 144. That gives
// 19 pixels of space on each side of the content, so the prefix is
// positioned at 19, and the text is at 19+100-16=103.
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)).
width
,
800.0
);
expect
(
tester
.
getSize
(
find
.
byType
(
InputDecorator
)).
height
,
closeTo
(
144
,
.
0001
));
expect
(
tester
.
getSize
(
find
.
text
(
'text'
)).
height
,
20.0
);
expect
(
tester
.
getSize
(
find
.
byKey
(
pKey
)).
height
,
100.0
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'text'
)).
dy
,
closeTo
(
103
,
.
0001
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
pKey
)).
dy
,
19.0
);
// layout is a row: [prefix text suffix]
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
pKey
)).
dx
,
12.0
);
expect
(
tester
.
getTopRight
(
find
.
byKey
(
pKey
)).
dx
,
tester
.
getTopLeft
(
find
.
text
(
'text'
)).
dx
);
});
testWidgets
(
'InputDecorator prefixIcon/suffixIcon'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildInputDecorator
(
...
...
@@ -1169,6 +1075,7 @@ void main() {
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
prefixKey
)).
dy
,
0.0
);
});
testWidgets
(
'counter text has correct right margin - LTR, not dense'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildInputDecorator
(
...
...
@@ -1556,12 +1463,12 @@ void main() {
testWidgets
(
'InputDecoration outline shape with no border and no floating placeholder'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildInputDecorator
(
// isFocused: false (default)
isEmpty:
true
,
decoration:
const
InputDecoration
(
border:
OutlineInputBorder
(
borderSide:
BorderSide
.
none
),
hasFloatingPlaceholder:
false
,
labelText:
'label'
,
// isFocused: false (default)
isEmpty:
true
,
decoration:
const
InputDecoration
(
border:
OutlineInputBorder
(
borderSide:
BorderSide
.
none
),
hasFloatingPlaceholder:
false
,
labelText:
'label'
,
),
),
);
...
...
packages/flutter/test/material/text_field_focus_test.dart
View file @
f21497ee
...
...
@@ -6,40 +6,24 @@ import 'package:flutter_test/flutter_test.dart';
import
'package:flutter/material.dart'
;
void
main
(
)
{
testWidgets
(
'
Dialog interaction
'
,
(
WidgetTester
tester
)
async
{
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
testWidgets
(
'
Request focus shows keyboard
'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
(
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(
autofocus:
tru
e
,
focusNode:
focusNod
e
,
),
),
),
),
);
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
final
BuildContext
context
=
tester
.
element
(
find
.
byType
(
TextField
));
showDialog
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
=>
const
SimpleDialog
(
title:
Text
(
'Dialog'
)),
);
await
tester
.
pump
();
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
Navigator
.
of
(
tester
.
element
(
find
.
text
(
'Dialog'
))).
pop
();
await
tester
.
pump
();
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
FocusScope
.
of
(
tester
.
element
(
find
.
byType
(
TextField
))).
requestFocus
(
focusNode
);
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
...
...
@@ -49,26 +33,21 @@ void main() {
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
});
testWidgets
(
'
Request
focus shows keyboard'
,
(
WidgetTester
tester
)
async
{
final
FocusNode
focusNode
=
FocusNode
(
);
testWidgets
(
'
Auto
focus shows keyboard'
,
(
WidgetTester
tester
)
async
{
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
pumpWidget
(
MaterialApp
(
const
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(
focusNode:
focusNod
e
,
autofocus:
tru
e
,
),
),
),
),
);
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
FocusScope
.
of
(
tester
.
element
(
find
.
byType
(
TextField
))).
requestFocus
(
focusNode
);
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
await
tester
.
pumpWidget
(
Container
());
...
...
@@ -76,21 +55,33 @@ void main() {
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
});
testWidgets
(
'
Autofocus
shows keyboard'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'
Tap
shows keyboard'
,
(
WidgetTester
tester
)
async
{
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(
autofocus:
true
,
),
child:
TextField
(),
),
),
),
);
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
tester
.
testTextInput
.
hide
();
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
await
tester
.
pumpWidget
(
Container
());
...
...
@@ -98,27 +89,36 @@ void main() {
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
});
testWidgets
(
'
Tap shows keyboard
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'
Dialog interaction
'
,
(
WidgetTester
tester
)
async
{
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
await
tester
.
pumpWidget
(
const
MaterialApp
(
home:
Material
(
child:
Center
(
child:
TextField
(),
child:
TextField
(
autofocus:
true
,
),
),
),
),
);
expect
(
tester
.
testTextInput
.
isVisible
,
is
Fals
e
);
expect
(
tester
.
testTextInput
.
isVisible
,
is
Tru
e
);
await
tester
.
tap
(
find
.
byType
(
TextField
));
await
tester
.
idle
();
final
BuildContext
context
=
tester
.
element
(
find
.
byType
(
TextField
));
expect
(
tester
.
testTextInput
.
isVisible
,
isTrue
);
showDialog
<
void
>(
context:
context
,
builder:
(
BuildContext
context
)
=>
const
SimpleDialog
(
title:
Text
(
'Dialog'
)),
);
tester
.
testTextInput
.
hide
();
await
tester
.
pump
();
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
Navigator
.
of
(
tester
.
element
(
find
.
text
(
'Dialog'
))).
pop
();
await
tester
.
pump
();
expect
(
tester
.
testTextInput
.
isVisible
,
isFalse
);
...
...
packages/flutter/test/material/text_field_test.dart
View file @
f21497ee
...
...
@@ -174,24 +174,6 @@ void main() {
debugResetSemanticsIdCounter
();
});
final
Key
textFieldKey
=
UniqueKey
();
Widget
textFieldBuilder
({
int
maxLines
=
1
,
int
minLines
,
})
{
return
boilerplate
(
child:
TextField
(
key:
textFieldKey
,
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
34.0
),
maxLines:
maxLines
,
minLines:
minLines
,
decoration:
const
InputDecoration
(
hintText:
'Placeholder'
,
),
),
);
}
testWidgets
(
'TextField passes onEditingComplete to EditableText'
,
(
WidgetTester
tester
)
async
{
final
VoidCallback
onEditingComplete
=
()
{
};
...
...
@@ -901,8 +883,23 @@ void main() {
expect
(
controller
.
selection
.
isCollapsed
,
false
);
});
testWidgets
(
'TextField height with minLines unset'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
textFieldBuilder
());
testWidgets
(
'Multiline text will wrap up to maxLines'
,
(
WidgetTester
tester
)
async
{
final
Key
textFieldKey
=
UniqueKey
();
Widget
builder
(
int
maxLines
)
{
return
boilerplate
(
child:
TextField
(
key:
textFieldKey
,
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
34.0
),
maxLines:
maxLines
,
decoration:
const
InputDecoration
(
hintText:
'Placeholder'
,
),
),
);
}
await
tester
.
pumpWidget
(
builder
(
null
));
RenderBox
findInputBox
()
=>
tester
.
renderObject
(
find
.
byKey
(
textFieldKey
));
...
...
@@ -910,278 +907,46 @@ void main() {
final
Size
emptyInputSize
=
inputBox
.
size
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'No wrapping here.'
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
));
await
tester
.
pumpWidget
(
builder
(
null
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
equals
(
emptyInputSize
));
// Even when entering multiline text, TextField doesn't grow. It's a single
// line input.
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kThreeLines
);
await
tester
.
pumpWidget
(
textFieldBuilder
());
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
equals
(
emptyInputSize
));
// maxLines: 3 makes the TextField 3 lines tall
await
tester
.
enterText
(
find
.
byType
(
TextField
),
''
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
maxLines:
3
));
await
tester
.
pumpWidget
(
builder
(
3
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
greaterThan
(
emptyInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
emptyInputSize
.
width
);
expect
(
inputBox
.
size
,
greaterThan
(
emptyInputSize
));
final
Size
threeLineInputSize
=
inputBox
.
size
;
// Filling with 3 lines of text stays the same size
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kThreeLines
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
maxLines:
3
));
await
tester
.
pumpWidget
(
builder
(
null
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
greaterThan
(
emptyInputSize
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kThreeLines
);
await
tester
.
pumpWidget
(
builder
(
null
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
threeLineInputSize
);
// An extra line won't increase the size because we max at 3.
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
maxLines:
3
));
await
tester
.
pumpWidget
(
builder
(
3
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
threeLineInputSize
);
// But now it will... but it will max at four
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
maxLines:
4
));
await
tester
.
pumpWidget
(
builder
(
4
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
greaterThan
(
threeLineInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
threeLineInputSize
.
width
);
expect
(
inputBox
.
size
,
greaterThan
(
threeLineInputSize
));
final
Size
fourLineInputSize
=
inputBox
.
size
;
// Now it won't max out until the end
await
tester
.
enterText
(
find
.
byType
(
TextField
),
''
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
maxLines:
null
));
await
tester
.
pumpWidget
(
builder
(
null
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
equals
(
emptyInputSize
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kThreeLines
);
await
tester
.
pump
();
expect
(
inputBox
.
size
,
equals
(
threeLineInputSize
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pump
();
expect
(
inputBox
.
size
.
height
,
greaterThan
(
fourLineInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
fourLineInputSize
.
width
);
expect
(
inputBox
.
size
,
greaterThan
(
fourLineInputSize
));
});
testWidgets
(
'TextField height with minLines and maxLines'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
textFieldBuilder
());
RenderBox
findInputBox
()
=>
tester
.
renderObject
(
find
.
byKey
(
textFieldKey
));
final
RenderBox
inputBox
=
findInputBox
();
final
Size
emptyInputSize
=
inputBox
.
size
;
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'No wrapping here.'
);
await
tester
.
pumpWidget
(
textFieldBuilder
());
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
equals
(
emptyInputSize
));
// min and max set to same value locks height to value.
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
,
maxLines:
3
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
greaterThan
(
emptyInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
emptyInputSize
.
width
);
final
Size
threeLineInputSize
=
inputBox
.
size
;
// maxLines: null with minLines set grows beyond minLines
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
,
maxLines:
null
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
threeLineInputSize
);
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pump
();
expect
(
inputBox
.
size
.
height
,
greaterThan
(
threeLineInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
threeLineInputSize
.
width
);
// With minLines and maxLines set, input will expand through the range
await
tester
.
enterText
(
find
.
byType
(
TextField
),
''
);
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
,
maxLines:
4
));
expect
(
findInputBox
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
,
equals
(
threeLineInputSize
));
await
tester
.
enterText
(
find
.
byType
(
TextField
),
kMoreThanFourLines
);
await
tester
.
pump
();
expect
(
inputBox
.
size
.
height
,
greaterThan
(
threeLineInputSize
.
height
));
expect
(
inputBox
.
size
.
width
,
threeLineInputSize
.
width
);
// minLines can't be greater than maxLines.
expect
(()
async
{
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
,
maxLines:
2
));
},
throwsAssertionError
);
expect
(()
async
{
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
));
},
throwsAssertionError
);
// maxLines defaults to 1 and can't be less than minLines
expect
(()
async
{
await
tester
.
pumpWidget
(
textFieldBuilder
(
minLines:
3
));
},
throwsAssertionError
);
});
testWidgets
(
'Multiline text when wrapped in Expanded'
,
(
WidgetTester
tester
)
async
{
Widget
expandedTextFieldBuilder
({
int
maxLines
=
1
,
int
minLines
,
bool
expands
=
false
,
})
{
return
boilerplate
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
<
Widget
>[
Expanded
(
child:
TextField
(
key:
textFieldKey
,
style:
const
TextStyle
(
color:
Colors
.
black
,
fontSize:
34.0
),
maxLines:
maxLines
,
minLines:
minLines
,
expands:
expands
,
decoration:
const
InputDecoration
(
hintText:
'Placeholder'
,
),
),
),
],
),
);
}
await
tester
.
pumpWidget
(
expandedTextFieldBuilder
());
RenderBox
findBorder
()
{
return
tester
.
renderObject
(
find
.
descendant
(
of:
find
.
byType
(
InputDecorator
),
matching:
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_BorderContainer'
),
));
}
final
RenderBox
border
=
findBorder
();
// Without expanded: true and maxLines: null, the TextField does not expand
// to fill its parent when wrapped in an Expanded widget.
final
Size
unexpandedInputSize
=
border
.
size
;
// It does expand to fill its parent when expands: true, maxLines: null, and
// it's wrapped in an Expanded widget.
await
tester
.
pumpWidget
(
expandedTextFieldBuilder
(
expands:
true
,
maxLines:
null
));
expect
(
border
.
size
.
height
,
greaterThan
(
unexpandedInputSize
.
height
));
expect
(
border
.
size
.
width
,
unexpandedInputSize
.
width
);
// min/maxLines that is not null and expands: true contradict each other.
expect
(()
async
{
await
tester
.
pumpWidget
(
expandedTextFieldBuilder
(
expands:
true
,
maxLines:
4
));
},
throwsAssertionError
);
expect
(()
async
{
await
tester
.
pumpWidget
(
expandedTextFieldBuilder
(
expands:
true
,
minLines:
1
,
maxLines:
null
));
},
throwsAssertionError
);
});
testWidgets
(
'Growable TextField when content height exceeds parent'
,
(
WidgetTester
tester
)
async
{
const
double
height
=
200.0
;
const
double
padding
=
24.0
;
Widget
containedTextFieldBuilder
({
Widget
counter
,
String
helperText
,
String
labelText
,
Widget
prefix
,
})
{
return
boilerplate
(
child:
Container
(
height:
height
,
child:
TextField
(
key:
textFieldKey
,
maxLines:
null
,
decoration:
InputDecoration
(
counter:
counter
,
helperText:
helperText
,
labelText:
labelText
,
prefix:
prefix
,
),
),
),
);
}
await
tester
.
pumpWidget
(
containedTextFieldBuilder
());
RenderBox
findEditableText
()
=>
tester
.
renderObject
(
find
.
byType
(
EditableText
));
final
RenderBox
inputBox
=
findEditableText
();
// With no decoration and when overflowing with content, the EditableText
// takes up the full height minus the padding, so the input fits perfectly
// inside the parent.
await
tester
.
enterText
(
find
.
byType
(
TextField
),
'a
\n
'
*
11
);
await
tester
.
pump
();
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
);
// Adding a counter causes the EditableText to shrink to fit the counter
// inside the parent as well.
const
double
counterHeight
=
40.0
;
const
double
subtextGap
=
8.0
*
2
;
const
double
counterSpace
=
counterHeight
+
subtextGap
;
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
counter:
Container
(
height:
counterHeight
),
));
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
counterSpace
);
// Including helperText causes the EditableText to shrink to fit the text
// inside the parent as well.
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
helperText:
'I am helperText'
,
));
expect
(
findEditableText
(),
equals
(
inputBox
));
const
double
helperTextSpace
=
28.0
;
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
helperTextSpace
);
// When both helperText and counter are present, EditableText shrinks by the
// height of the taller of the two in order to fit both within the parent.
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
counter:
Container
(
height:
counterHeight
),
helperText:
'I am helperText'
,
));
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
counterSpace
);
// When a label is present, EditableText shrinks to fit it at the top so
// that the bottom of the input still lines up perfectly with the parent.
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
labelText:
'I am labelText'
,
));
const
double
labelSpace
=
16.0
;
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
labelSpace
);
// When decoration is present on the top and bottom, EditableText shrinks to
// fit both inside the parent independently.
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
counter:
Container
(
height:
counterHeight
),
labelText:
'I am labelText'
,
));
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
counterSpace
-
labelSpace
);
// When a prefix or suffix is present in an input that's full of content,
// it is ignored and allowed to expand beyond the top of the input. Other
// top and bottom decoration is still respected.
await
tester
.
pumpWidget
(
containedTextFieldBuilder
(
counter:
Container
(
height:
counterHeight
),
labelText:
'I am labelText'
,
prefix:
Container
(
width:
10
,
height:
60
,
),
));
expect
(
findEditableText
(),
equals
(
inputBox
));
expect
(
inputBox
.
size
.
height
,
height
-
padding
-
labelSpace
-
counterSpace
,
);
});
testWidgets
(
'Multiline hint text will wrap up to maxLines'
,
(
WidgetTester
tester
)
async
{
final
Key
textFieldKey
=
UniqueKey
();
...
...
packages/flutter/test/rendering/editable_test.dart
View file @
f21497ee
...
...
@@ -56,7 +56,6 @@ void main() {
' │ cursorColor: null
\n
'
' │ showCursor: ValueNotifier<bool>#00000(false)
\n
'
' │ maxLines: 1
\n
'
' │ minLines: null
\n
'
' │ selectionColor: null
\n
'
' │ textScaleFactor: 1.0
\n
'
' │ locale: ja_JP
\n
'
...
...
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