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
0d945a1a
Unverified
Commit
0d945a1a
authored
Sep 17, 2020
by
xubaolin
Committed by
GitHub
Sep 17, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix the inconsistency between the local state of the input and the engine state (#65754)
parent
827cbc35
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
146 additions
and
10 deletions
+146
-10
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+20
-10
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+126
-0
No files found.
packages/flutter/lib/src/widgets/editable_text.dart
View file @
0d945a1a
...
...
@@ -1620,11 +1620,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
@override
TextEditingValue
get
currentTextEditingValue
=>
_value
;
bool
_updateEditingValueInProgress
=
false
;
@override
void
updateEditingValue
(
TextEditingValue
value
)
{
_updateEditingValueInProgress
=
true
;
// Since we still have to support keyboard select, this is the best place
// to disable text updating.
if
(!
_shouldCreateInputConnection
)
{
_updateEditingValueInProgress
=
false
;
return
;
}
if
(
widget
.
readOnly
)
{
...
...
@@ -1643,7 +1647,14 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
}
if
(
_isSelectionOnlyChange
(
value
))
{
if
(
value
==
_value
)
{
// This is possible, for example, when the numeric keyboard is input,
// the engine will notify twice for the same value.
// Track at https://github.com/flutter/flutter/issues/65811
_updateEditingValueInProgress
=
false
;
return
;
}
else
if
(
value
.
text
==
_value
.
text
&&
value
.
composing
==
_value
.
composing
&&
value
.
selection
!=
_value
.
selection
)
{
// `selection` is the only change.
_handleSelectionChanged
(
value
.
selection
,
renderEditable
!,
SelectionChangedCause
.
keyboard
);
}
else
{
_formatAndSetValue
(
value
);
...
...
@@ -1655,10 +1666,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_stopCursorTimer
(
resetCharTicks:
false
);
_startCursorTimer
();
}
}
bool
_isSelectionOnlyChange
(
TextEditingValue
value
)
{
return
value
.
text
==
_value
.
text
&&
value
.
composing
==
_value
.
composing
;
_updateEditingValueInProgress
=
false
;
}
@override
...
...
@@ -1815,8 +1823,14 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if
(!
_hasInputConnection
)
return
;
final
TextEditingValue
localValue
=
_value
;
if
(
localValue
==
_receivedRemoteTextEditingValue
)
// We should not update back the value notified by the remote(engine) in reverse, this is redundant.
// Unless we modify this value for some reason during processing, such as `TextInputFormatter`.
if
(
_updateEditingValueInProgress
&&
localValue
==
_receivedRemoteTextEditingValue
)
return
;
// In other cases, as long as the value of the [widget.controller.value] is modified,
// `setEditingState` should be called as we do not want to skip sending real changes
// to the engine.
// Also see https://github.com/flutter/flutter/issues/65059#issuecomment-690254379
_textInputConnection
!.
setEditingState
(
localValue
);
}
...
...
@@ -2140,10 +2154,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_value
=
_lastFormattedValue
!;
}
// Always attempt to send the value. If the value has changed, then it will send,
// otherwise, it will short-circuit.
_updateRemoteEditingValueIfNeeded
();
if
(
textChanged
&&
widget
.
onChanged
!=
null
)
widget
.
onChanged
!(
value
.
text
);
_lastFormattedUnmodifiedTextEditingValue
=
_receivedRemoteTextEditingValue
;
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
0d945a1a
...
...
@@ -4713,6 +4713,132 @@ void main() {
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
'flutter is the best!...'
);
});
testWidgets
(
'Synchronous test of local and remote editing values'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/65059
final
List
<
MethodCall
>
log
=
<
MethodCall
>[];
SystemChannels
.
textInput
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
log
.
add
(
methodCall
);
});
final
TextInputFormatter
formatter
=
TextInputFormatter
.
withFunction
((
TextEditingValue
oldValue
,
TextEditingValue
newValue
)
{
if
(
newValue
.
text
==
'I will be modified by the formatter.'
)
{
newValue
=
const
TextEditingValue
(
text:
'Flutter is the best!'
);
}
return
newValue
;
});
final
TextEditingController
controller
=
TextEditingController
();
StateSetter
setState
;
final
FocusNode
focusNode
=
FocusNode
(
debugLabel:
'EditableText Focus Node'
);
Widget
builder
()
{
return
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
return
MaterialApp
(
home:
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
Material
(
child:
EditableText
(
controller:
controller
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
Colors
.
red
,
backgroundCursorColor:
Colors
.
red
,
keyboardType:
TextInputType
.
multiline
,
inputFormatters:
<
TextInputFormatter
>[
formatter
,
],
onChanged:
(
String
value
)
{
},
),
),
),
),
),
);
},
);
}
await
tester
.
pumpWidget
(
builder
());
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
await
tester
.
pump
();
log
.
clear
();
final
EditableTextState
state
=
tester
.
firstState
(
find
.
byType
(
EditableText
));
// setEditingState is not called when only the remote changes
state
.
updateEditingValue
(
const
TextEditingValue
(
text:
'a'
,
));
expect
(
log
.
length
,
0
);
// setEditingState is called when remote value modified by the formatter.
state
.
updateEditingValue
(
const
TextEditingValue
(
text:
'I will be modified by the formatter.'
,
));
expect
(
log
.
length
,
1
);
MethodCall
methodCall
=
log
[
0
];
expect
(
methodCall
,
isMethodCall
(
'TextInput.setEditingState'
,
arguments:
<
String
,
dynamic
>{
'text'
:
'Flutter is the best!'
,
'selectionBase'
:
-
1
,
'selectionExtent'
:
-
1
,
'selectionAffinity'
:
'TextAffinity.downstream'
,
'selectionIsDirectional'
:
false
,
'composingBase'
:
-
1
,
'composingExtent'
:
-
1
,
}),
);
log
.
clear
();
// setEditingState is called when the [controller.value] is modified by local.
setState
(()
{
controller
.
text
=
'I love flutter!'
;
});
expect
(
log
.
length
,
1
);
methodCall
=
log
[
0
];
expect
(
methodCall
,
isMethodCall
(
'TextInput.setEditingState'
,
arguments:
<
String
,
dynamic
>{
'text'
:
'I love flutter!'
,
'selectionBase'
:
-
1
,
'selectionExtent'
:
-
1
,
'selectionAffinity'
:
'TextAffinity.downstream'
,
'selectionIsDirectional'
:
false
,
'composingBase'
:
-
1
,
'composingExtent'
:
-
1
,
}),
);
log
.
clear
();
// Currently `_receivedRemoteTextEditingValue` equals 'I will be modified by the formatter.',
// setEditingState will be called when set the [controller.value] to `_receivedRemoteTextEditingValue` by local.
setState
(()
{
controller
.
text
=
'I will be modified by the formatter.'
;
});
expect
(
log
.
length
,
1
);
methodCall
=
log
[
0
];
expect
(
methodCall
,
isMethodCall
(
'TextInput.setEditingState'
,
arguments:
<
String
,
dynamic
>{
'text'
:
'I will be modified by the formatter.'
,
'selectionBase'
:
-
1
,
'selectionExtent'
:
-
1
,
'selectionAffinity'
:
'TextAffinity.downstream'
,
'selectionIsDirectional'
:
false
,
'composingBase'
:
-
1
,
'composingExtent'
:
-
1
,
}),
);
});
testWidgets
(
'autofocus:true on first frame does not throw'
,
(
WidgetTester
tester
)
async
{
final
TextEditingController
controller
=
TextEditingController
(
text:
testText
);
controller
.
selection
=
const
TextSelection
(
...
...
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