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
2b67846a
Unverified
Commit
2b67846a
authored
Aug 19, 2020
by
Mouad Debbar
Committed by
GitHub
Aug 19, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make SelectableText work better on web (#63786)
parent
414f8b59
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
105 additions
and
3 deletions
+105
-3
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+7
-0
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+23
-3
text_input_test.dart
packages/flutter/test/services/text_input_test.dart
+4
-0
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+71
-0
No files found.
packages/flutter/lib/src/services/text_input.dart
View file @
2b67846a
...
@@ -447,6 +447,7 @@ class TextInputConfiguration {
...
@@ -447,6 +447,7 @@ class TextInputConfiguration {
/// [actionLabel] may be null.
/// [actionLabel] may be null.
const
TextInputConfiguration
({
const
TextInputConfiguration
({
this
.
inputType
=
TextInputType
.
text
,
this
.
inputType
=
TextInputType
.
text
,
this
.
readOnly
=
false
,
this
.
obscureText
=
false
,
this
.
obscureText
=
false
,
this
.
autocorrect
=
true
,
this
.
autocorrect
=
true
,
SmartDashesType
?
smartDashesType
,
SmartDashesType
?
smartDashesType
,
...
@@ -470,6 +471,11 @@ class TextInputConfiguration {
...
@@ -470,6 +471,11 @@ class TextInputConfiguration {
/// The type of information for which to optimize the text input control.
/// The type of information for which to optimize the text input control.
final
TextInputType
inputType
;
final
TextInputType
inputType
;
/// Whether the text field can be edited or not.
///
/// Defaults to false.
final
bool
readOnly
;
/// Whether to hide the text being edited (e.g., for passwords).
/// Whether to hide the text being edited (e.g., for passwords).
///
///
/// Defaults to false.
/// Defaults to false.
...
@@ -580,6 +586,7 @@ class TextInputConfiguration {
...
@@ -580,6 +586,7 @@ class TextInputConfiguration {
Map
<
String
,
dynamic
>
toJson
()
{
Map
<
String
,
dynamic
>
toJson
()
{
return
<
String
,
dynamic
>{
return
<
String
,
dynamic
>{
'inputType'
:
inputType
.
toJson
(),
'inputType'
:
inputType
.
toJson
(),
'readOnly'
:
readOnly
,
'obscureText'
:
obscureText
,
'obscureText'
:
obscureText
,
'autocorrect'
:
autocorrect
,
'autocorrect'
:
autocorrect
,
'smartDashesType'
:
smartDashesType
.
index
.
toString
(),
'smartDashesType'
:
smartDashesType
.
index
.
toString
(),
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
2b67846a
...
@@ -484,6 +484,10 @@ class EditableText extends StatefulWidget {
...
@@ -484,6 +484,10 @@ class EditableText extends StatefulWidget {
assert
(
dragStartBehavior
!=
null
),
assert
(
dragStartBehavior
!=
null
),
assert
(
toolbarOptions
!=
null
),
assert
(
toolbarOptions
!=
null
),
assert
(
clipBehavior
!=
null
),
assert
(
clipBehavior
!=
null
),
assert
(
!
readOnly
||
autofillHints
==
null
,
"Read-only fields can't have autofill hints."
,
),
_strutStyle
=
strutStyle
,
_strutStyle
=
strutStyle
,
keyboardType
=
keyboardType
??
_inferKeyboardType
(
autofillHints:
autofillHints
,
maxLines:
maxLines
),
keyboardType
=
keyboardType
??
_inferKeyboardType
(
autofillHints:
autofillHints
,
maxLines:
maxLines
),
inputFormatters
=
maxLines
==
1
inputFormatters
=
maxLines
==
1
...
@@ -1437,6 +1441,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1437,6 +1441,21 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
// Is this field in the current autofill context.
// Is this field in the current autofill context.
bool
_isInAutofillContext
=
false
;
bool
_isInAutofillContext
=
false
;
/// Whether to create an input connection with the platform for text editing
/// or not.
///
/// Read-only input fields do not need a connection with the platform since
/// there's no need for text editing capabilities (e.g. virtual keyboard).
///
/// On the web, we always need a connection because we want some browser
/// functionalities to continue to work on read-only input fields like:
///
/// - Relevant context menu.
/// - cmd/ctrl+c shortcut to copy.
/// - cmd/ctrl+a to select all.
/// - Changing the selection using a physical keyboard.
bool
get
_shouldCreateInputConnection
=>
kIsWeb
||
!
widget
.
readOnly
;
// This value is an eyeball estimation of the time it takes for the iOS cursor
// This value is an eyeball estimation of the time it takes for the iOS cursor
// to ease in and out.
// to ease in and out.
static
const
Duration
_fadeDuration
=
Duration
(
milliseconds:
250
);
static
const
Duration
_fadeDuration
=
Duration
(
milliseconds:
250
);
...
@@ -1531,7 +1550,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1531,7 +1550,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
widget
.
focusNode
.
addListener
(
_handleFocusChanged
);
widget
.
focusNode
.
addListener
(
_handleFocusChanged
);
updateKeepAlive
();
updateKeepAlive
();
}
}
if
(
widget
.
readOnly
)
{
if
(
!
_shouldCreateInputConnection
)
{
_closeInputConnectionIfNeeded
();
_closeInputConnectionIfNeeded
();
}
else
{
}
else
{
if
(
oldWidget
.
readOnly
&&
_hasFocus
)
if
(
oldWidget
.
readOnly
&&
_hasFocus
)
...
@@ -1597,7 +1616,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1597,7 +1616,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
void
updateEditingValue
(
TextEditingValue
value
)
{
void
updateEditingValue
(
TextEditingValue
value
)
{
// Since we still have to support keyboard select, this is the best place
// Since we still have to support keyboard select, this is the best place
// to disable text updating.
// to disable text updating.
if
(
widget
.
readOnly
)
{
if
(
!
_shouldCreateInputConnection
)
{
return
;
return
;
}
}
_receivedRemoteTextEditingValue
=
value
;
_receivedRemoteTextEditingValue
=
value
;
...
@@ -1846,7 +1865,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -1846,7 +1865,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
bool
get
_shouldBeInAutofillContext
=>
_needsAutofill
&&
currentAutofillScope
!=
null
;
bool
get
_shouldBeInAutofillContext
=>
_needsAutofill
&&
currentAutofillScope
!=
null
;
void
_openInputConnection
()
{
void
_openInputConnection
()
{
if
(
widget
.
readOnly
)
{
if
(
!
_shouldCreateInputConnection
)
{
return
;
return
;
}
}
if
(!
_hasInputConnection
)
{
if
(!
_hasInputConnection
)
{
...
@@ -2305,6 +2324,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
...
@@ -2305,6 +2324,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
assert
(
needsAutofillConfiguration
!=
null
);
assert
(
needsAutofillConfiguration
!=
null
);
return
TextInputConfiguration
(
return
TextInputConfiguration
(
inputType:
widget
.
keyboardType
,
inputType:
widget
.
keyboardType
,
readOnly:
widget
.
readOnly
,
obscureText:
widget
.
obscureText
,
obscureText:
widget
.
obscureText
,
autocorrect:
widget
.
autocorrect
,
autocorrect:
widget
.
autocorrect
,
smartDashesType:
widget
.
smartDashesType
??
(
widget
.
obscureText
?
SmartDashesType
.
disabled
:
SmartDashesType
.
enabled
),
smartDashesType:
widget
.
smartDashesType
??
(
widget
.
obscureText
?
SmartDashesType
.
disabled
:
SmartDashesType
.
enabled
),
...
...
packages/flutter/test/services/text_input_test.dart
View file @
2b67846a
...
@@ -73,6 +73,7 @@ void main() {
...
@@ -73,6 +73,7 @@ void main() {
test
(
'sets expected defaults'
,
()
{
test
(
'sets expected defaults'
,
()
{
const
TextInputConfiguration
configuration
=
TextInputConfiguration
();
const
TextInputConfiguration
configuration
=
TextInputConfiguration
();
expect
(
configuration
.
inputType
,
TextInputType
.
text
);
expect
(
configuration
.
inputType
,
TextInputType
.
text
);
expect
(
configuration
.
readOnly
,
false
);
expect
(
configuration
.
obscureText
,
false
);
expect
(
configuration
.
obscureText
,
false
);
expect
(
configuration
.
autocorrect
,
true
);
expect
(
configuration
.
autocorrect
,
true
);
expect
(
configuration
.
actionLabel
,
null
);
expect
(
configuration
.
actionLabel
,
null
);
...
@@ -83,6 +84,7 @@ void main() {
...
@@ -83,6 +84,7 @@ void main() {
test
(
'text serializes to JSON'
,
()
async
{
test
(
'text serializes to JSON'
,
()
async
{
const
TextInputConfiguration
configuration
=
TextInputConfiguration
(
const
TextInputConfiguration
configuration
=
TextInputConfiguration
(
inputType:
TextInputType
.
text
,
inputType:
TextInputType
.
text
,
readOnly:
true
,
obscureText:
true
,
obscureText:
true
,
autocorrect:
false
,
autocorrect:
false
,
actionLabel:
'xyzzy'
,
actionLabel:
'xyzzy'
,
...
@@ -93,6 +95,7 @@ void main() {
...
@@ -93,6 +95,7 @@ void main() {
'signed'
:
null
,
'signed'
:
null
,
'decimal'
:
null
,
'decimal'
:
null
,
});
});
expect
(
json
[
'readOnly'
],
true
);
expect
(
json
[
'obscureText'
],
true
);
expect
(
json
[
'obscureText'
],
true
);
expect
(
json
[
'autocorrect'
],
false
);
expect
(
json
[
'autocorrect'
],
false
);
expect
(
json
[
'actionLabel'
],
'xyzzy'
);
expect
(
json
[
'actionLabel'
],
'xyzzy'
);
...
@@ -111,6 +114,7 @@ void main() {
...
@@ -111,6 +114,7 @@ void main() {
'signed'
:
false
,
'signed'
:
false
,
'decimal'
:
true
,
'decimal'
:
true
,
});
});
expect
(
json
[
'readOnly'
],
false
);
expect
(
json
[
'obscureText'
],
true
);
expect
(
json
[
'obscureText'
],
true
);
expect
(
json
[
'autocorrect'
],
false
);
expect
(
json
[
'autocorrect'
],
false
);
expect
(
json
[
'actionLabel'
],
'xyzzy'
);
expect
(
json
[
'actionLabel'
],
'xyzzy'
);
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
2b67846a
...
@@ -1302,6 +1302,77 @@ void main() {
...
@@ -1302,6 +1302,77 @@ void main() {
expect
(
find
.
text
(
'Cut'
),
findsNothing
);
expect
(
find
.
text
(
'Cut'
),
findsNothing
);
});
});
testWidgets
(
'Handles the read-only flag correctly'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
'Lorem ipsum dolor sit amet'
);
await
tester
.
pumpWidget
(
MaterialApp
(
home:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
);
final
EditableTextState
state
=
tester
.
state
<
EditableTextState
>(
find
.
byType
(
EditableText
));
// Select the first word "Lorem".
state
.
renderEditable
.
selectWordsInRange
(
from:
Offset
.
zero
,
cause:
SelectionChangedCause
.
tap
,
);
if
(
kIsWeb
)
{
// On the web, a regular connection to the platform should've been made
// with the `readOnly` flag set to true.
expect
(
tester
.
testTextInput
.
hasAnyClients
,
isTrue
);
expect
(
tester
.
testTextInput
.
setClientArgs
[
'readOnly'
],
isTrue
);
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
equals
(
'Lorem'
));
}
else
{
// On non-web platforms, a read-only field doesn't need a connection with
// the platform.
expect
(
tester
.
testTextInput
.
hasAnyClients
,
isFalse
);
}
});
testWidgets
(
'Does not accept updates when read-only'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
'Lorem ipsum dolor sit amet'
);
await
tester
.
pumpWidget
(
MaterialApp
(
home:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
),
),
);
final
EditableTextState
state
=
tester
.
state
<
EditableTextState
>(
find
.
byType
(
EditableText
));
// Select something.
state
.
renderEditable
.
selectWordsInRange
(
from:
Offset
.
zero
,
cause:
SelectionChangedCause
.
tap
,
);
expect
(
tester
.
testTextInput
.
hasAnyClients
,
kIsWeb
?
isTrue
:
isFalse
);
if
(
kIsWeb
)
{
// On the web, the input connection exists, but text updates should be
// ignored.
tester
.
testTextInput
.
enterText
(
'Foo bar'
);
// No change.
expect
(
controller
.
text
,
'Lorem ipsum dolor sit amet'
);
}
});
testWidgets
(
'Fires onChanged when text changes via TextSelectionOverlay'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Fires onChanged when text changes via TextSelectionOverlay'
,
(
WidgetTester
tester
)
async
{
String
changedValue
;
String
changedValue
;
final
Widget
widget
=
MaterialApp
(
final
Widget
widget
=
MaterialApp
(
...
...
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