Fixing a text editing bug happening when text field changes. (#41108)

* Carrying last size and transform information to TextInputConnection. Doing this we are making sure that the size/transform information from the previous connection will be removed, when connection changes.

* remove unused lastsize and lasttransform values

* Adding unit tests. Adressing comments.
This commit is contained in:
Nurhan Turgut 2019-09-24 09:25:39 -07:00 committed by GitHub
parent 0e6cb28dbe
commit f6e77a9ee5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 18 deletions

View file

@ -642,8 +642,11 @@ abstract class TextInputClient {
/// * [TextInput.attach]
class TextInputConnection {
TextInputConnection._(this._client)
: assert(_client != null),
_id = _nextId++;
: assert(_client != null),
_id = _nextId++;
Size _cachedSize;
Matrix4 _cachedTransform;
static int _nextId = 1;
final int _id;
@ -678,14 +681,18 @@ class TextInputConnection {
/// 2. [transform]: a matrix that maps the local paint coordinate system
/// to the [PipelineOwner.rootNode].
void setEditableSizeAndTransform(Size editableBoxSize, Matrix4 transform) {
SystemChannels.textInput.invokeMethod<void>(
'TextInput.setEditableSizeAndTransform',
<String, dynamic>{
'width': editableBoxSize.width,
'height': editableBoxSize.height,
'transform': transform.storage,
},
);
if (editableBoxSize != _cachedSize || transform != _cachedTransform) {
_cachedSize = editableBoxSize;
_cachedTransform = transform;
SystemChannels.textInput.invokeMethod<void>(
'TextInput.setEditableSizeAndTransform',
<String, dynamic>{
'width': editableBoxSize.width,
'height': editableBoxSize.height,
'transform': transform.storage,
},
);
}
}
/// Send text styling information.

View file

@ -1703,18 +1703,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
updateKeepAlive();
}
Size _lastSize;
Matrix4 _lastTransform;
void _updateSizeAndTransform() {
if (_hasInputConnection) {
final Size size = renderEditable.size;
final Matrix4 transform = renderEditable.getTransformTo(null);
if (size != _lastSize || transform != _lastTransform) {
_lastSize = size;
_lastTransform = transform;
_textInputConnection.setEditableSizeAndTransform(size, transform);
}
_textInputConnection.setEditableSizeAndTransform(size, transform);
SchedulerBinding.instance
.addPostFrameCallback((Duration _) => _updateSizeAndTransform());
}

View file

@ -2215,6 +2215,88 @@ void main() {
);
});
testWidgets('transform and size is reset when text connection opens', (WidgetTester tester) async {
final List<MethodCall> log = <MethodCall>[];
SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async {
log.add(methodCall);
});
final TextEditingController controller1 = TextEditingController();
final TextEditingController controller2 = TextEditingController();
controller1.text = 'Text1';
controller2.text = 'Text2';
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(
devicePixelRatio: 1.0
),
child: Directionality(
textDirection: TextDirection.ltr,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
EditableText(
key: ValueKey<String>(controller1.text),
controller: controller1,
focusNode: FocusNode(),
style: Typography(platform: TargetPlatform.android).black.subhead,
cursorColor: Colors.blue,
backgroundCursorColor: Colors.grey,
),
const SizedBox(height: 200.0),
EditableText(
key: ValueKey<String>(controller2.text),
controller: controller2,
focusNode: FocusNode(),
style: Typography(platform: TargetPlatform.android).black.subhead,
cursorColor: Colors.blue,
backgroundCursorColor: Colors.grey,
),
const SizedBox(height: 100.0),
],
),
),
),
);
await tester.showKeyboard(find.byKey(ValueKey<String>(controller1.text)));
final MethodCall methodCall = log.firstWhere((MethodCall m) => m.method == 'TextInput.setEditableSizeAndTransform');
expect(
methodCall,
isMethodCall('TextInput.setEditableSizeAndTransform', arguments: <String, dynamic>{
'width': 800,
'height': 14,
'transform': Matrix4.identity().storage.toList(),
}),
);
// Move to the next editable text.
await tester.showKeyboard(find.byKey(ValueKey<String>(controller2.text)));
final MethodCall methodCall2 = log.firstWhere((MethodCall m) => m.method == 'TextInput.setEditableSizeAndTransform');
expect(
methodCall2,
isMethodCall('TextInput.setEditableSizeAndTransform', arguments: <String, dynamic>{
'width': 800,
'height': 14,
'transform': Matrix4.identity().storage.toList(),
}),
);
// Move back to the first editable text.
await tester.showKeyboard(find.byKey(ValueKey<String>(controller1.text)));
final MethodCall methodCall3 = log.firstWhere((MethodCall m) => m.method == 'TextInput.setEditableSizeAndTransform');
expect(
methodCall3,
isMethodCall('TextInput.setEditableSizeAndTransform', arguments: <String, dynamic>{
'width': 800,
'height': 14,
'transform': Matrix4.identity().storage.toList(),
}),
);
});
testWidgets('size and transform are sent when they change', (WidgetTester tester) async {
final List<MethodCall> log = <MethodCall>[];
SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async {