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
a7b28a3e
Commit
a7b28a3e
authored
Mar 25, 2016
by
Matt Perry
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added a Form widget to manage multiple Input widgets.
parent
03830d56
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
393 additions
and
76 deletions
+393
-76
text_field_demo.dart
examples/material_gallery/lib/demo/text_field_demo.dart
+66
-67
input.dart
packages/flutter/lib/src/material/input.dart
+58
-9
form.dart
packages/flutter/lib/src/widgets/form.dart
+100
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
form_test.dart
packages/flutter/test/widget/form_test.dart
+168
-0
No files found.
examples/material_gallery/lib/demo/text_field_demo.dart
View file @
a7b28a3e
...
@@ -11,14 +11,16 @@ class TextFieldDemo extends StatefulWidget {
...
@@ -11,14 +11,16 @@ class TextFieldDemo extends StatefulWidget {
TextFieldDemoState
createState
()
=>
new
TextFieldDemoState
();
TextFieldDemoState
createState
()
=>
new
TextFieldDemoState
();
}
}
class
PersonData
{
String
name
;
String
phoneNumber
;
String
password
;
}
class
TextFieldDemoState
extends
State
<
TextFieldDemo
>
{
class
TextFieldDemoState
extends
State
<
TextFieldDemo
>
{
final
GlobalKey
<
ScaffoldState
>
_scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
final
GlobalKey
<
ScaffoldState
>
_scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
final
List
<
InputValue
>
_inputs
=
<
InputValue
>[
InputValue
.
empty
,
PersonData
person
=
new
PersonData
();
InputValue
.
empty
,
InputValue
.
empty
,
InputValue
.
empty
,
];
void
showInSnackBar
(
String
value
)
{
void
showInSnackBar
(
String
value
)
{
_scaffoldKey
.
currentState
.
showSnackBar
(
new
SnackBar
(
_scaffoldKey
.
currentState
.
showSnackBar
(
new
SnackBar
(
...
@@ -26,36 +28,30 @@ class TextFieldDemoState extends State<TextFieldDemo> {
...
@@ -26,36 +28,30 @@ class TextFieldDemoState extends State<TextFieldDemo> {
));
));
}
}
void
_handleInputChanged
(
InputValue
value
,
int
which
)
{
void
_handleSubmitted
()
{
setState
(()
{
showInSnackBar
(
'
${person.name}
\'
s phone number is
${person.phoneNumber}
'
);
_inputs
[
which
]
=
value
;
});
}
void
_handleInputSubmitted
(
InputValue
value
)
{
showInSnackBar
(
'
${_inputs[0].text}
\'
s phone number is
${_inputs[1].text}
'
);
}
}
String
_validateName
(
InputValue
value
)
{
String
_validateName
(
String
value
)
{
if
(
value
.
text
.
isEmpty
)
if
(
value
.
isEmpty
)
return
'Name is required.'
;
return
'Name is required.'
;
RegExp
nameExp
=
new
RegExp
(
r'^[A-za-z ]+$'
);
RegExp
nameExp
=
new
RegExp
(
r'^[A-za-z ]+$'
);
if
(!
nameExp
.
hasMatch
(
value
.
text
))
if
(!
nameExp
.
hasMatch
(
value
))
return
'Please enter only alphabetical characters.'
;
return
'Please enter only alphabetical characters.'
;
return
null
;
return
null
;
}
}
String
_validatePhoneNumber
(
InputValue
value
)
{
String
_validatePhoneNumber
(
String
value
)
{
RegExp
phoneExp
=
new
RegExp
(
r'^\d\d\d-\d\d\d\-\d\d\d\d$'
);
RegExp
phoneExp
=
new
RegExp
(
r'^\d\d\d-\d\d\d\-\d\d\d\d$'
);
if
(!
phoneExp
.
hasMatch
(
value
.
text
))
if
(!
phoneExp
.
hasMatch
(
value
))
return
'###-###-#### - Please enter a valid phone number.'
;
return
'###-###-#### - Please enter a valid phone number.'
;
return
null
;
return
null
;
}
}
String
_validatePassword
(
InputValue
value1
,
InputValue
value2
)
{
String
_validatePassword
(
String
value
)
{
if
(
value1
.
text
.
isEmpty
)
if
(
person
.
password
==
null
||
person
.
password
.
isEmpty
)
return
'Please choose a password.'
;
return
'Please choose a password.'
;
if
(
value1
.
text
!=
value2
.
text
)
if
(
person
.
password
!=
value
)
return
'Passwords don
\'
t match'
;
return
'Passwords don
\'
t match'
;
return
null
;
return
null
;
}
}
...
@@ -67,24 +63,27 @@ class TextFieldDemoState extends State<TextFieldDemo> {
...
@@ -67,24 +63,27 @@ class TextFieldDemoState extends State<TextFieldDemo> {
appBar:
new
AppBar
(
appBar:
new
AppBar
(
title:
new
Text
(
'Text Fields'
)
title:
new
Text
(
'Text Fields'
)
),
),
body:
new
Block
(
body:
new
Form
(
onSubmitted:
_handleSubmitted
,
child:
new
Block
(
padding:
const
EdgeInsets
.
all
(
8.0
),
padding:
const
EdgeInsets
.
all
(
8.0
),
children:
<
Widget
>[
children:
<
Widget
>[
new
Input
(
new
Input
(
hintText:
'What do people call you?'
,
hintText:
'What do people call you?'
,
labelText:
'Name'
,
labelText:
'Name'
,
errorText:
_validateName
(
_inputs
[
0
]),
formField:
new
FormField
<
String
>(
value:
_inputs
[
0
],
// TODO(mpcomplete): replace with person#name=
onChanged:
(
InputValue
value
)
{
_handleInputChanged
(
value
,
0
);
},
setter:
(
String
val
)
{
person
.
name
=
val
;
},
onSubmitted:
_handleInputSubmitted
validator:
_validateName
)
),
),
new
Input
(
new
Input
(
hintText:
'Where can we reach you?'
,
hintText:
'Where can we reach you?'
,
labelText:
'Phone Number'
,
labelText:
'Phone Number'
,
errorText:
_validatePhoneNumber
(
_inputs
[
1
]),
formField:
new
FormField
<
String
>(
value:
_inputs
[
1
]
,
setter:
(
String
val
)
{
person
.
phoneNumber
=
val
;
}
,
onChanged:
(
InputValue
value
)
{
_handleInputChanged
(
value
,
1
);
},
validator:
_validatePhoneNumber
onSubmitted:
_handleInputSubmitted
)
),
),
new
Row
(
new
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
crossAxisAlignment:
CrossAxisAlignment
.
start
,
...
@@ -94,26 +93,26 @@ class TextFieldDemoState extends State<TextFieldDemo> {
...
@@ -94,26 +93,26 @@ class TextFieldDemoState extends State<TextFieldDemo> {
hintText:
'How do you log in?'
,
hintText:
'How do you log in?'
,
labelText:
'New Password'
,
labelText:
'New Password'
,
hideText:
true
,
hideText:
true
,
value:
_inputs
[
2
],
formField:
new
FormField
<
String
>(
onChanged:
(
InputValue
value
)
{
_handleInputChanged
(
value
,
2
);
},
setter:
(
String
val
)
{
person
.
password
=
val
;
}
onSubmitted:
_handleInputSubmitted
)
)
)
),
),
new
Flexible
(
new
Flexible
(
child:
new
Input
(
child:
new
Input
(
hintText:
'How do you log in?'
,
hintText:
'How do you log in?'
,
labelText:
'Re-type Password'
,
labelText:
'Re-type Password'
,
errorText:
_validatePassword
(
_inputs
[
2
],
_inputs
[
3
]),
hideText:
true
,
hideText:
true
,
value:
_inputs
[
3
],
formField:
new
FormField
<
String
>(
onChanged:
(
InputValue
value
)
{
_handleInputChanged
(
value
,
3
);
},
validator:
_validatePassword
onSubmitted:
_handleInputSubmitted
)
)
)
)
)
]
]
)
)
]
]
)
)
)
);
);
}
}
}
}
packages/flutter/lib/src/material/input.dart
View file @
a7b28a3e
...
@@ -17,7 +17,7 @@ export 'package:sky_services/editing/editing.mojom.dart' show KeyboardType;
...
@@ -17,7 +17,7 @@ export 'package:sky_services/editing/editing.mojom.dart' show KeyboardType;
class
Input
extends
StatefulWidget
{
class
Input
extends
StatefulWidget
{
Input
({
Input
({
Key
key
,
Key
key
,
this
.
value
:
InputValue
.
empty
,
this
.
value
,
this
.
keyboardType
:
KeyboardType
.
text
,
this
.
keyboardType
:
KeyboardType
.
text
,
this
.
icon
,
this
.
icon
,
this
.
labelText
,
this
.
labelText
,
...
@@ -27,6 +27,7 @@ class Input extends StatefulWidget {
...
@@ -27,6 +27,7 @@ class Input extends StatefulWidget {
this
.
hideText
:
false
,
this
.
hideText
:
false
,
this
.
isDense
:
false
,
this
.
isDense
:
false
,
this
.
autofocus
:
false
,
this
.
autofocus
:
false
,
this
.
formField
,
this
.
onChanged
,
this
.
onChanged
,
this
.
onSubmitted
this
.
onSubmitted
})
:
super
(
key:
key
);
})
:
super
(
key:
key
);
...
@@ -61,6 +62,9 @@ class Input extends StatefulWidget {
...
@@ -61,6 +62,9 @@ class Input extends StatefulWidget {
/// Whether this input field should focus itself is nothing else is already focused.
/// Whether this input field should focus itself is nothing else is already focused.
final
bool
autofocus
;
final
bool
autofocus
;
/// Form-specific data, required if this Input is part of a Form.
final
FormField
<
String
>
formField
;
/// Called when the text being edited changes.
/// Called when the text being edited changes.
final
ValueChanged
<
InputValue
>
onChanged
;
final
ValueChanged
<
InputValue
>
onChanged
;
...
@@ -79,12 +83,24 @@ class _InputState extends State<Input> {
...
@@ -79,12 +83,24 @@ class _InputState extends State<Input> {
GlobalKey
get
focusKey
=>
config
.
key
is
GlobalKey
?
config
.
key
:
_rawInputLineKey
;
GlobalKey
get
focusKey
=>
config
.
key
is
GlobalKey
?
config
.
key
:
_rawInputLineKey
;
// Optional state to retain if we are inside a Form widget.
_FormFieldData
_formData
;
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMaterial
(
context
));
assert
(
debugCheckHasMaterial
(
context
));
ThemeData
themeData
=
Theme
.
of
(
context
);
ThemeData
themeData
=
Theme
.
of
(
context
);
BuildContext
focusContext
=
focusKey
.
currentContext
;
BuildContext
focusContext
=
focusKey
.
currentContext
;
bool
focused
=
focusContext
!=
null
&&
Focus
.
at
(
focusContext
,
autofocus:
config
.
autofocus
);
bool
focused
=
focusContext
!=
null
&&
Focus
.
at
(
focusContext
,
autofocus:
config
.
autofocus
);
if
(
_formData
==
null
)
_formData
=
_FormFieldData
.
maybeCreate
(
context
,
this
);
InputValue
value
=
config
.
value
??
_formData
?.
value
??
InputValue
.
empty
;
ValueChanged
<
InputValue
>
onChanged
=
config
.
onChanged
??
_formData
?.
onChanged
;
ValueChanged
<
InputValue
>
onSubmitted
=
config
.
onSubmitted
??
_formData
?.
onSubmitted
;
String
errorText
=
config
.
errorText
;
if
(
errorText
==
null
&&
config
.
formField
!=
null
&&
config
.
formField
.
validator
!=
null
)
errorText
=
config
.
formField
.
validator
(
value
.
text
);
TextStyle
textStyle
=
config
.
style
??
themeData
.
textTheme
.
subhead
;
TextStyle
textStyle
=
config
.
style
??
themeData
.
textTheme
.
subhead
;
Color
activeColor
=
themeData
.
hintColor
;
Color
activeColor
=
themeData
.
hintColor
;
...
@@ -102,7 +118,7 @@ class _InputState extends State<Input> {
...
@@ -102,7 +118,7 @@ class _InputState extends State<Input> {
List
<
Widget
>
stackChildren
=
<
Widget
>[];
List
<
Widget
>
stackChildren
=
<
Widget
>[];
bool
hasInlineLabel
=
config
.
labelText
!=
null
&&
!
focused
&&
!
config
.
value
.
text
.
isNotEmpty
;
bool
hasInlineLabel
=
config
.
labelText
!=
null
&&
!
focused
&&
!
value
.
text
.
isNotEmpty
;
if
(
config
.
labelText
!=
null
)
{
if
(
config
.
labelText
!=
null
)
{
TextStyle
labelStyle
=
hasInlineLabel
?
TextStyle
labelStyle
=
hasInlineLabel
?
...
@@ -125,7 +141,7 @@ class _InputState extends State<Input> {
...
@@ -125,7 +141,7 @@ class _InputState extends State<Input> {
topPadding
+=
topPaddingIncrement
;
topPadding
+=
topPaddingIncrement
;
}
}
if
(
config
.
hintText
!=
null
&&
config
.
value
.
text
.
isEmpty
&&
!
hasInlineLabel
)
{
if
(
config
.
hintText
!=
null
&&
value
.
text
.
isEmpty
&&
!
hasInlineLabel
)
{
TextStyle
hintStyle
=
themeData
.
textTheme
.
subhead
.
copyWith
(
color:
themeData
.
hintColor
);
TextStyle
hintStyle
=
themeData
.
textTheme
.
subhead
.
copyWith
(
color:
themeData
.
hintColor
);
stackChildren
.
add
(
new
Positioned
(
stackChildren
.
add
(
new
Positioned
(
left:
0.0
,
left:
0.0
,
...
@@ -139,7 +155,7 @@ class _InputState extends State<Input> {
...
@@ -139,7 +155,7 @@ class _InputState extends State<Input> {
Color
borderColor
=
activeColor
;
Color
borderColor
=
activeColor
;
double
borderWidth
=
focused
?
2.0
:
1.0
;
double
borderWidth
=
focused
?
2.0
:
1.0
;
if
(
config
.
errorText
!=
null
)
{
if
(
errorText
!=
null
)
{
borderColor
=
themeData
.
errorColor
;
borderColor
=
themeData
.
errorColor
;
borderWidth
=
2.0
;
borderWidth
=
2.0
;
if
(!
config
.
isDense
)
{
if
(!
config
.
isDense
)
{
...
@@ -163,24 +179,24 @@ class _InputState extends State<Input> {
...
@@ -163,24 +179,24 @@ class _InputState extends State<Input> {
),
),
child:
new
RawInputLine
(
child:
new
RawInputLine
(
key:
_rawInputLineKey
,
key:
_rawInputLineKey
,
value:
config
.
value
,
value:
value
,
focusKey:
focusKey
,
focusKey:
focusKey
,
style:
textStyle
,
style:
textStyle
,
hideText:
config
.
hideText
,
hideText:
config
.
hideText
,
cursorColor:
themeData
.
selectionColor
,
cursorColor:
themeData
.
selectionColor
,
selectionColor:
themeData
.
selectionColor
,
selectionColor:
themeData
.
selectionColor
,
keyboardType:
config
.
keyboardType
,
keyboardType:
config
.
keyboardType
,
onChanged:
config
.
onChanged
,
onChanged:
onChanged
,
onSubmitted:
config
.
onSubmitted
onSubmitted:
onSubmitted
)
)
));
));
if
(
config
.
errorText
!=
null
&&
!
config
.
isDense
)
{
if
(
errorText
!=
null
&&
!
config
.
isDense
)
{
TextStyle
errorStyle
=
themeData
.
textTheme
.
caption
.
copyWith
(
color:
themeData
.
errorColor
);
TextStyle
errorStyle
=
themeData
.
textTheme
.
caption
.
copyWith
(
color:
themeData
.
errorColor
);
stackChildren
.
add
(
new
Positioned
(
stackChildren
.
add
(
new
Positioned
(
left:
0.0
,
left:
0.0
,
bottom:
0.0
,
bottom:
0.0
,
child:
new
Text
(
config
.
errorText
,
style:
errorStyle
)
child:
new
Text
(
errorText
,
style:
errorStyle
)
));
));
}
}
...
@@ -216,3 +232,36 @@ class _InputState extends State<Input> {
...
@@ -216,3 +232,36 @@ class _InputState extends State<Input> {
);
);
}
}
}
}
class
_FormFieldData
{
_FormFieldData
(
this
.
inputState
)
{
assert
(
field
!=
null
);
}
InputValue
value
=
new
InputValue
();
final
_InputState
inputState
;
FormField
<
String
>
get
field
=>
inputState
.
config
.
formField
;
static
_FormFieldData
maybeCreate
(
BuildContext
context
,
_InputState
inputState
)
{
// Only create a _FormFieldData if this Input is a descendent of a Form.
if
(
FormScope
.
of
(
context
)
!=
null
)
return
new
_FormFieldData
(
inputState
);
return
null
;
}
void
onChanged
(
InputValue
value
)
{
FormScope
scope
=
FormScope
.
of
(
inputState
.
context
);
assert
(
scope
!=
null
);
this
.
value
=
value
;
if
(
field
.
setter
!=
null
)
field
.
setter
(
value
.
text
);
scope
.
onFieldChanged
();
}
void
onSubmitted
(
InputValue
value
)
{
FormScope
scope
=
FormScope
.
of
(
inputState
.
context
);
assert
(
scope
!=
null
);
scope
.
form
.
onSubmitted
();
scope
.
onFieldChanged
();
}
}
packages/flutter/lib/src/widgets/form.dart
0 → 100644
View file @
a7b28a3e
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'basic.dart'
;
import
'framework.dart'
;
/// A container for grouping together multiple form field widgets (e.g. Input).
class
Form
extends
StatefulWidget
{
Form
({
Key
key
,
this
.
child
,
this
.
onSubmitted
})
:
super
(
key:
key
)
{
assert
(
child
!=
null
);
}
/// Called when the input is accepted anywhere on the form.
final
VoidCallback
onSubmitted
;
/// Root of the widget hierarchy that contains this form.
final
Widget
child
;
@override
_FormState
createState
()
=>
new
_FormState
();
}
class
_FormState
extends
State
<
Form
>
{
int
generation
=
0
;
void
onFieldChanged
()
{
setState
(()
{
++
generation
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
FormScope
(
state:
this
,
generation:
generation
,
child:
config
.
child
);
}
}
typedef
String
FormFieldValidator
<
T
>(
T
value
);
typedef
void
FormFieldSetter
<
T
>(
T
newValue
);
/// This contains identifying information for Input fields, required if the
/// Input is part of a Form.
class
FormField
<
T
>
{
FormField
({
this
.
setter
,
this
.
validator
});
/// An optional method to call with the new value when the form field changes.
final
FormFieldSetter
<
T
>
setter
;
/// An optional method that validates an input. Returns an error string to
/// display if the input is invalid, or null otherwise.
final
FormFieldValidator
<
T
>
validator
;
}
/// The root of all Forms. Used by form field widgets (e.g. Input) to
/// communicate changes back to the client.
class
FormScope
extends
InheritedWidget
{
FormScope
({
Key
key
,
Widget
child
,
_FormState
state
,
int
generation
})
:
_state
=
state
,
_generation
=
generation
,
super
(
key:
key
,
child:
child
);
final
_FormState
_state
;
/// Incremented every time a form field has changed. This lets us know when
/// to rebuild the form.
final
int
_generation
;
/// The Form this widget belongs to.
Form
get
form
=>
_state
.
config
;
/// Finds the FormScope that encloses the widget being built from the given
/// context.
static
FormScope
of
(
BuildContext
context
)
{
return
context
.
inheritFromWidgetOfExactType
(
FormScope
);
}
/// Use this to notify the Form that a form field has changed. This will
/// cause all form fields to rebuild, useful if form fields have
/// interdependencies.
void
onFieldChanged
()
=>
_state
.
onFieldChanged
();
@override
bool
updateShouldNotify
(
FormScope
old
)
=>
_generation
!=
old
.
_generation
;
}
packages/flutter/lib/widgets.dart
View file @
a7b28a3e
...
@@ -17,6 +17,7 @@ export 'src/widgets/dismissable.dart';
...
@@ -17,6 +17,7 @@ export 'src/widgets/dismissable.dart';
export
'src/widgets/drag_target.dart'
;
export
'src/widgets/drag_target.dart'
;
export
'src/widgets/editable.dart'
;
export
'src/widgets/editable.dart'
;
export
'src/widgets/focus.dart'
;
export
'src/widgets/focus.dart'
;
export
'src/widgets/form.dart'
;
export
'src/widgets/framework.dart'
;
export
'src/widgets/framework.dart'
;
export
'src/widgets/gesture_detector.dart'
;
export
'src/widgets/gesture_detector.dart'
;
export
'src/widgets/gridpaper.dart'
;
export
'src/widgets/gridpaper.dart'
;
...
...
packages/flutter/test/widget/form_test.dart
0 → 100644
View file @
a7b28a3e
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:sky_services/editing/editing.mojom.dart'
as
mojom
;
import
'package:test/test.dart'
;
class
MockKeyboard
implements
mojom
.
Keyboard
{
mojom
.
KeyboardClient
client
;
@override
void
setClient
(
mojom
.
KeyboardClientStub
client
,
mojom
.
KeyboardConfiguration
configuraiton
)
{
this
.
client
=
client
.
impl
;
}
@override
void
show
()
{}
@override
void
hide
()
{}
@override
void
setEditingState
(
mojom
.
EditingState
state
)
{}
}
void
main
(
)
{
WidgetFlutterBinding
.
ensureInitialized
();
// for serviceMocker
MockKeyboard
mockKeyboard
=
new
MockKeyboard
();
serviceMocker
.
registerMockService
(
mojom
.
Keyboard
.
serviceName
,
mockKeyboard
);
void
enterText
(
String
testValue
)
{
// Simulate entry of text through the keyboard.
expect
(
mockKeyboard
.
client
,
isNotNull
);
mockKeyboard
.
client
.
updateEditingState
(
new
mojom
.
EditingState
()
..
text
=
testValue
..
composingBase
=
0
..
composingExtent
=
testValue
.
length
);
}
test
(
'Setter callback is called'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
inputKey
=
new
GlobalKey
();
String
fieldValue
;
Widget
builder
()
{
return
new
Center
(
child:
new
Material
(
child:
new
Form
(
child:
new
Input
(
key:
inputKey
,
formField:
new
FormField
<
String
>(
setter:
(
String
val
)
{
fieldValue
=
val
;
}
)
)
)
)
);
}
tester
.
pumpWidget
(
builder
());
void
checkText
(
String
testValue
)
{
enterText
(
testValue
);
// Check that the FormField's setter was called.
expect
(
fieldValue
,
equals
(
testValue
));
tester
.
pumpWidget
(
builder
());
}
checkText
(
'Test'
);
checkText
(
''
);
});
});
test
(
'Validator sets the error text'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
inputKey
=
new
GlobalKey
();
String
errorText
(
String
input
)
=>
input
+
'/error'
;
Widget
builder
()
{
return
new
Center
(
child:
new
Material
(
child:
new
Form
(
child:
new
Input
(
key:
inputKey
,
formField:
new
FormField
<
String
>(
validator:
errorText
)
)
)
)
);
}
tester
.
pumpWidget
(
builder
());
void
checkErrorText
(
String
testValue
)
{
enterText
(
testValue
);
tester
.
pumpWidget
(
builder
());
// Check for a new Text widget with our error text.
Element
errorElement
=
tester
.
findText
(
errorText
(
testValue
));
expect
(
errorElement
,
isNotNull
);
}
checkErrorText
(
'Test'
);
checkErrorText
(
''
);
});
});
test
(
'Multiple Inputs communicate'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
GlobalKey
inputKey
=
new
GlobalKey
();
GlobalKey
focusKey
=
new
GlobalKey
();
// Input 1's text value.
String
fieldValue
;
// Input 2's validator depends on a input 1's value.
String
errorText
(
String
input
)
=>
fieldValue
.
toString
()
+
'/error'
;
Widget
builder
()
{
return
new
Center
(
child:
new
Material
(
child:
new
Form
(
child:
new
Focus
(
key:
focusKey
,
child:
new
Block
(
children:
<
Widget
>[
new
Input
(
key:
inputKey
,
formField:
new
FormField
<
String
>(
setter:
(
String
val
)
{
fieldValue
=
val
;
}
)
),
new
Input
(
formField:
new
FormField
<
String
>(
validator:
errorText
)
)
]
)
)
)
)
);
}
tester
.
pumpWidget
(
builder
());
Focus
.
moveTo
(
inputKey
);
tester
.
pump
();
void
checkErrorText
(
String
testValue
)
{
enterText
(
testValue
);
tester
.
pumpWidget
(
builder
());
expect
(
fieldValue
,
equals
(
testValue
));
// Check for a new Text widget with our error text.
Element
errorElement
=
tester
.
findText
(
errorText
(
testValue
));
expect
(
errorElement
,
isNotNull
);
}
checkErrorText
(
'Test'
);
checkErrorText
(
''
);
});
});
}
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