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
74dd0a3a
Commit
74dd0a3a
authored
Nov 07, 2016
by
Hans Muller
Committed by
GitHub
Nov 07, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Input refactoring: added an InputField widget (#6733)
parent
fb3bf7a9
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
242 additions
and
59 deletions
+242
-59
input.dart
packages/flutter/lib/src/material/input.dart
+166
-43
form_test.dart
packages/flutter/test/widgets/form_test.dart
+14
-16
input_test.dart
packages/flutter/test/widgets/input_test.dart
+62
-0
No files found.
packages/flutter/lib/src/material/input.dart
View file @
74dd0a3a
...
...
@@ -16,6 +16,125 @@ import 'theme.dart';
export
'package:flutter/services.dart'
show
TextInputType
;
class
InputField
extends
StatefulWidget
{
InputField
({
Key
key
,
this
.
focusKey
,
this
.
value
,
this
.
keyboardType
:
TextInputType
.
text
,
this
.
hintText
,
this
.
style
,
this
.
hideText
:
false
,
this
.
maxLines
:
1
,
this
.
onChanged
,
this
.
onSubmitted
,
})
:
super
(
key:
key
);
final
GlobalKey
focusKey
;
/// The current state of text of the input field. This includes the selected
/// text, if any, among other things.
final
InputValue
value
;
/// The type of keyboard to use for editing the text.
final
TextInputType
keyboardType
;
/// Text to show inline in the input field when it would otherwise be empty.
final
String
hintText
;
/// The style to use for the text being edited.
final
TextStyle
style
;
/// Whether to hide the text being edited (e.g., for passwords).
///
/// When this is set to true, all the characters in the input are replaced by
/// U+2022 BULLET characters (•).
final
bool
hideText
;
/// The maximum number of lines for the text to span, wrapping if necessary.
/// If this is 1 (the default), the text will not wrap, but will scroll
/// horizontally instead.
final
int
maxLines
;
/// Called when the text being edited changes.
///
/// The [value] must be updated each time [onChanged] is invoked.
final
ValueChanged
<
InputValue
>
onChanged
;
/// Called when the user indicates that they are done editing the text in the field.
final
ValueChanged
<
InputValue
>
onSubmitted
;
@override
_InputFieldState
createState
()
=>
new
_InputFieldState
();
}
class
_InputFieldState
extends
State
<
InputField
>
{
GlobalKey
<
RawInputState
>
_rawInputKey
=
new
GlobalKey
<
RawInputState
>();
GlobalKey
<
RawInputState
>
_focusKey
=
new
GlobalKey
(
debugLabel:
"_InputFieldState _focusKey"
);
GlobalKey
get
focusKey
=>
config
.
focusKey
??
(
config
.
key
is
GlobalKey
?
config
.
key
:
_focusKey
);
void
requestKeyboard
()
{
_rawInputKey
.
currentState
?.
requestKeyboard
();
}
@override
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMaterial
(
context
));
final
InputValue
value
=
config
.
value
??
InputValue
.
empty
;
final
ThemeData
themeData
=
Theme
.
of
(
context
);
final
TextStyle
textStyle
=
config
.
style
??
themeData
.
textTheme
.
subhead
;
final
List
<
Widget
>
stackChildren
=
<
Widget
>[
new
GestureDetector
(
key:
focusKey
==
_focusKey
?
_focusKey
:
null
,
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
requestKeyboard
();
},
// Since the focusKey may have been created here, defer building the
// RawInput until the focusKey's context has been set. This is necessary
// because the RawInput will check the focus, like Focus.at(focusContext),
// when it builds.
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
RawInput
(
key:
_rawInputKey
,
value:
value
,
focusKey:
focusKey
,
style:
textStyle
,
hideText:
config
.
hideText
,
maxLines:
config
.
maxLines
,
cursorColor:
themeData
.
textSelectionColor
,
selectionColor:
themeData
.
textSelectionColor
,
selectionControls:
materialTextSelectionControls
,
platform:
Theme
.
of
(
context
).
platform
,
keyboardType:
config
.
keyboardType
,
onChanged:
config
.
onChanged
,
onSubmitted:
config
.
onSubmitted
,
);
}
),
),
];
if
(
config
.
hintText
!=
null
&&
value
.
text
.
isEmpty
)
{
TextStyle
hintStyle
=
themeData
.
textTheme
.
subhead
.
copyWith
(
color:
themeData
.
hintColor
);
stackChildren
.
add
(
new
Positioned
(
left:
0.0
,
top:
textStyle
.
fontSize
-
hintStyle
.
fontSize
,
child:
new
IgnorePointer
(
child:
new
Text
(
config
.
hintText
,
style:
hintStyle
),
),
),
);
}
return
new
RepaintBoundary
(
child:
new
Stack
(
children:
stackChildren
));
}
}
/// A material design text input field.
///
/// Requires one of its ancestors to be a [Material] widget.
...
...
@@ -116,9 +235,10 @@ const Duration _kTransitionDuration = const Duration(milliseconds: 200);
const
Curve
_kTransitionCurve
=
Curves
.
fastOutSlowIn
;
class
_InputState
extends
State
<
Input
>
{
GlobalKey
<
RawInputState
>
_rawInputKey
=
new
GlobalKey
<
RawInputState
>();
GlobalKey
<
_InputFieldState
>
_inputFieldKey
=
new
GlobalKey
<
_InputFieldState
>(
debugLabel:
'_InputState _inputFieldKey'
);
GlobalKey
<
_InputFieldState
>
_focusKey
=
new
GlobalKey
(
debugLabel:
'_InputState _focusKey'
);
GlobalKey
get
focusKey
=>
config
.
key
is
GlobalKey
?
config
.
key
:
_
rawInput
Key
;
GlobalKey
get
focusKey
=>
config
.
key
is
GlobalKey
?
config
.
key
:
_
focus
Key
;
@override
Widget
build
(
BuildContext
context
)
{
...
...
@@ -145,38 +265,35 @@ class _InputState extends State<Input> {
List
<
Widget
>
stackChildren
=
<
Widget
>[];
bool
hasInlineLabel
=
config
.
labelText
!=
null
&&
!
focused
&&
!
value
.
text
.
isNotEmpty
;
final
bool
hasInlineLabel
=
config
.
labelText
!=
null
&&
!
focused
&&
!
value
.
text
.
isNotEmpty
;
if
(
config
.
labelText
!=
null
)
{
TextStyle
labelStyle
=
hasInlineLabel
?
final
TextStyle
labelStyle
=
hasInlineLabel
?
themeData
.
textTheme
.
subhead
.
copyWith
(
color:
themeData
.
hintColor
)
:
themeData
.
textTheme
.
caption
.
copyWith
(
color:
activeColor
);
double
topPaddingIncrement
=
themeData
.
textTheme
.
caption
.
fontSize
+
(
config
.
isDense
?
4.0
:
8.0
);
final
double
topPaddingIncrement
=
themeData
.
textTheme
.
caption
.
fontSize
+
(
config
.
isDense
?
4.0
:
8.0
);
double
top
=
topPadding
;
if
(
hasInlineLabel
)
top
+=
topPaddingIncrement
+
textStyle
.
fontSize
-
labelStyle
.
fontSize
;
stackChildren
.
add
(
new
AnimatedPositioned
(
left:
0.0
,
top:
top
,
duration:
_kTransitionDuration
,
curve:
_kTransitionCurve
,
child:
new
Text
(
config
.
labelText
,
style:
labelStyle
)
));
stackChildren
.
add
(
new
AnimatedPositioned
(
left:
0.0
,
top:
top
,
duration:
_kTransitionDuration
,
curve:
_kTransitionCurve
,
child:
new
AnimatedOpacity
(
opacity:
focused
?
1.0
:
0.0
,
curve:
_kTransitionCurve
,
duration:
_kTransitionDuration
,
child:
new
Text
(
config
.
labelText
,
style:
labelStyle
)
),
),
);
topPadding
+=
topPaddingIncrement
;
}
if
(
config
.
hintText
!=
null
&&
value
.
text
.
isEmpty
&&
!
hasInlineLabel
)
{
TextStyle
hintStyle
=
themeData
.
textTheme
.
subhead
.
copyWith
(
color:
themeData
.
hintColor
);
stackChildren
.
add
(
new
Positioned
(
left:
0.0
,
top:
topPadding
+
textStyle
.
fontSize
-
hintStyle
.
fontSize
,
child:
new
Text
(
config
.
hintText
,
style:
hintStyle
)
));
}
Color
borderColor
=
activeColor
;
double
bottomPadding
=
8.0
;
double
bottomBorder
=
focused
?
2.0
:
1.0
;
...
...
@@ -206,21 +323,26 @@ class _InputState extends State<Input> {
decoration:
new
BoxDecoration
(
border:
border
,
),
child:
new
RawInput
(
key:
_rawInputKey
,
value:
value
,
focusKey:
focusKey
,
style:
textStyle
,
hideText:
config
.
hideText
,
maxLines:
config
.
maxLines
,
cursorColor:
themeData
.
textSelectionColor
,
selectionColor:
themeData
.
textSelectionColor
,
selectionControls:
materialTextSelectionControls
,
platform:
Theme
.
of
(
context
).
platform
,
keyboardType:
config
.
keyboardType
,
onChanged:
config
.
onChanged
,
onSubmitted:
config
.
onSubmitted
,
)
// Since the focusKey may have been created here, defer building the
// InputField until the focusKey's context has been set. This is necessary
// because our descendants may check the focus, like Focus.at(focusContext),
// when they build.
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
InputField
(
key:
_inputFieldKey
,
focusKey:
focusKey
,
value:
value
,
style:
textStyle
,
hideText:
config
.
hideText
,
maxLines:
config
.
maxLines
,
keyboardType:
config
.
keyboardType
,
hintText:
config
.
hintText
,
onChanged:
config
.
onChanged
,
onSubmitted:
config
.
onSubmitted
,
);
}
),
));
if
(
errorText
!=
null
&&
!
config
.
isDense
)
{
...
...
@@ -257,12 +379,13 @@ class _InputState extends State<Input> {
);
}
return
new
RepaintBoundary
(
child:
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
=>
_rawInputKey
.
currentState
?.
requestKeyboard
(),
child:
child
)
return
new
GestureDetector
(
key:
config
.
key
is
GlobalKey
?
null
:
focusKey
,
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
_inputFieldKey
.
currentState
?.
requestKeyboard
();
},
child:
child
,
);
}
}
...
...
packages/flutter/test/widgets/form_test.dart
View file @
74dd0a3a
...
...
@@ -24,8 +24,8 @@ void main() {
child:
new
Form
(
key:
formKey
,
child:
new
InputFormField
(
onSaved:
(
InputValue
value
)
{
fieldValue
=
value
.
text
;
}
)
onSaved:
(
InputValue
value
)
{
fieldValue
=
value
.
text
;
}
,
)
,
)
)
);
...
...
@@ -57,8 +57,8 @@ void main() {
child:
new
Form
(
child:
new
InputFormField
(
key:
inputKey
,
validator:
errorText
)
validator:
errorText
,
)
,
)
)
);
...
...
@@ -101,11 +101,11 @@ void main() {
focusKey:
inputFocusKey
,
),
new
InputFormField
(
validator:
errorText
)
validator:
errorText
,
)
,
]
)
)
)
,
)
)
);
...
...
@@ -140,7 +140,7 @@ void main() {
child:
new
InputFormField
(
key:
inputKey
,
initialValue:
new
InputValue
(
text:
initialValue
),
)
)
,
)
)
);
...
...
@@ -175,14 +175,12 @@ void main() {
child:
new
Material
(
child:
new
Form
(
key:
formKey
,
child:
remove
?
new
Container
()
:
new
InputFormField
(
key:
fieldKey
,
autofocus:
true
,
onSaved:
(
InputValue
value
)
{
fieldValue
=
value
.
text
;
},
validator:
(
InputValue
value
)
{
return
value
.
text
.
isEmpty
?
null
:
'yes'
;
}
)
child:
remove
?
new
Container
()
:
new
InputFormField
(
key:
fieldKey
,
autofocus:
true
,
onSaved:
(
InputValue
value
)
{
fieldValue
=
value
.
text
;
},
validator:
(
InputValue
value
)
{
return
value
.
text
.
isEmpty
?
null
:
'yes'
;
}
),
)
)
);
...
...
packages/flutter/test/widgets/input_test.dart
View file @
74dd0a3a
...
...
@@ -673,4 +673,66 @@ void main() {
expect
(
inputBox
.
hitTest
(
new
HitTestResult
(),
position:
inputBox
.
globalToLocal
(
newFirstPos
)),
isTrue
);
expect
(
inputBox
.
hitTest
(
new
HitTestResult
(),
position:
inputBox
.
globalToLocal
(
newFourthPos
)),
isFalse
);
});
testWidgets
(
'InputField smoke test'
,
(
WidgetTester
tester
)
async
{
InputValue
inputValue
=
InputValue
.
empty
;
Widget
builder
()
{
return
new
Center
(
child:
new
Material
(
child:
new
InputField
(
value:
inputValue
,
hintText:
'Placeholder'
,
onChanged:
(
InputValue
value
)
{
inputValue
=
value
;
}
)
)
);
}
await
tester
.
pumpWidget
(
builder
());
Future
<
Null
>
checkText
(
String
testValue
)
async
{
enterText
(
testValue
);
await
tester
.
idle
();
// Check that the onChanged event handler fired.
expect
(
inputValue
.
text
,
equals
(
testValue
));
return
await
tester
.
pumpWidget
(
builder
());
}
checkText
(
'Hello World'
);
});
testWidgets
(
'InputField with global key'
,
(
WidgetTester
tester
)
async
{
GlobalKey
inputFieldKey
=
new
GlobalKey
(
debugLabel:
'inputFieldKey'
);
InputValue
inputValue
=
InputValue
.
empty
;
Widget
builder
()
{
return
new
Center
(
child:
new
Material
(
child:
new
InputField
(
key:
inputFieldKey
,
value:
inputValue
,
hintText:
'Placeholder'
,
onChanged:
(
InputValue
value
)
{
inputValue
=
value
;
}
)
)
);
}
await
tester
.
pumpWidget
(
builder
());
Future
<
Null
>
checkText
(
String
testValue
)
async
{
enterText
(
testValue
);
await
tester
.
idle
();
// Check that the onChanged event handler fired.
expect
(
inputValue
.
text
,
equals
(
testValue
));
return
await
tester
.
pumpWidget
(
builder
());
}
checkText
(
'Hello World'
);
});
}
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