Unverified Commit dbbaf68e authored by WenJingRui's avatar WenJingRui Committed by GitHub

Fix: fix the delay of showOnScreen animation when keyboard comes up. (#99546)

parent f1d8e30c
...@@ -1965,7 +1965,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1965,7 +1965,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
// to make sure the user can see the changes they just made. Programmatical // to make sure the user can see the changes they just made. Programmatical
// changes to `textEditingValue` do not trigger the behavior even if the // changes to `textEditingValue` do not trigger the behavior even if the
// text field is focused. // text field is focused.
_scheduleShowCaretOnScreen(); _scheduleShowCaretOnScreen(withAnimation: true);
if (_hasInputConnection) { if (_hasInputConnection) {
// To keep the cursor from blinking while typing, we want to restart the // To keep the cursor from blinking while typing, we want to restart the
// cursor timer every time a new character is typed. // cursor timer every time a new character is typed.
...@@ -2498,7 +2498,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2498,7 +2498,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
bool _showCaretOnScreenScheduled = false; bool _showCaretOnScreenScheduled = false;
void _scheduleShowCaretOnScreen() { void _scheduleShowCaretOnScreen({required bool withAnimation}) {
if (_showCaretOnScreenScheduled) { if (_showCaretOnScreenScheduled) {
return; return;
} }
...@@ -2538,17 +2538,23 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2538,17 +2538,23 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final RevealedOffset targetOffset = _getOffsetToRevealCaret(_currentCaretRect!); final RevealedOffset targetOffset = _getOffsetToRevealCaret(_currentCaretRect!);
_scrollController.animateTo( if (withAnimation) {
targetOffset.offset, _scrollController.animateTo(
duration: _caretAnimationDuration, targetOffset.offset,
curve: _caretAnimationCurve, duration: _caretAnimationDuration,
); curve: _caretAnimationCurve,
);
renderEditable.showOnScreen( renderEditable.showOnScreen(
rect: caretPadding.inflateRect(targetOffset.rect), rect: caretPadding.inflateRect(targetOffset.rect),
duration: _caretAnimationDuration, duration: _caretAnimationDuration,
curve: _caretAnimationCurve, curve: _caretAnimationCurve,
); );
} else {
_scrollController.jumpTo(targetOffset.offset);
renderEditable.showOnScreen(
rect: caretPadding.inflateRect(targetOffset.rect),
);
}
}); });
} }
...@@ -2561,7 +2567,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2561,7 +2567,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_selectionOverlay?.updateForScroll(); _selectionOverlay?.updateForScroll();
}); });
if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) { if (_lastBottomViewInset < WidgetsBinding.instance.window.viewInsets.bottom) {
_scheduleShowCaretOnScreen(); // Because the metrics change signal from engine will come here every frame
// (on both iOS and Android). So we don't need to show caret with animation.
_scheduleShowCaretOnScreen(withAnimation: false);
} }
} }
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
...@@ -2745,7 +2753,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2745,7 +2753,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom; _lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
if (!widget.readOnly) { if (!widget.readOnly) {
_scheduleShowCaretOnScreen(); _scheduleShowCaretOnScreen(withAnimation: true);
} }
if (!_value.selection.isValid) { if (!_value.selection.isValid) {
// Place cursor at the end if the selection is invalid when we receive focus. // Place cursor at the end if the selection is invalid when we receive focus.
...@@ -2900,7 +2908,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -2900,7 +2908,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
? _value.selection != value.selection ? _value.selection != value.selection
: _value != value; : _value != value;
if (shouldShowCaret) { if (shouldShowCaret) {
_scheduleShowCaretOnScreen(); _scheduleShowCaretOnScreen(withAnimation: true);
} }
_formatAndSetValue(value, cause, userInteraction: true); _formatAndSetValue(value, cause, userInteraction: true);
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
...@@ -39,6 +41,26 @@ class _MatchesMethodCall extends Matcher { ...@@ -39,6 +41,26 @@ class _MatchesMethodCall extends Matcher {
} }
} }
// Used to set window.viewInsets since the real ui.WindowPadding has only a
// private constructor.
class _TestWindowPadding implements ui.WindowPadding {
const _TestWindowPadding({
required this.bottom,
});
@override
final double bottom;
@override
double get top => 0.0;
@override
double get left => 0.0;
@override
double get right => 0.0;
}
late TextEditingController controller; late TextEditingController controller;
final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Node'); final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Node');
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'EditableText Scope Node'); final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'EditableText Scope Node');
...@@ -107,6 +129,51 @@ void main() { ...@@ -107,6 +129,51 @@ void main() {
expect(tester.testTextInput.setClientArgs!['inputAction'], equals(serializedActionName)); expect(tester.testTextInput.setClientArgs!['inputAction'], equals(serializedActionName));
} }
// Related issue: https://github.com/flutter/flutter/issues/98115
testWidgets('ScheduleShowCaretOnScreen with no animation when the window changes metrics', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
final Widget widget = MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
controller: scrollController,
child: Column(
children: <Widget>[
Column(
children: List<Widget>.generate(
5,
(_) {
return Container(
height: 1200.0,
color: Colors.black12,
);
},
),
),
SizedBox(
height: 20,
child: EditableText(
controller: TextEditingController(),
backgroundCursorColor: Colors.grey,
focusNode: focusNode,
style: const TextStyle(),
cursorColor: Colors.red,
),
),
],
),
),
),
);
await tester.pumpWidget(widget);
await tester.showKeyboard(find.byType(EditableText));
TestWidgetsFlutterBinding.instance.window.viewInsetsTestValue = const _TestWindowPadding(bottom: 500);
await tester.pump();
// The offset of the scrollController should change immediately after window changes its metrics.
final double offsetAfter = scrollController.offset;
expect(offsetAfter, isNot(0.0));
});
// Regression test for https://github.com/flutter/flutter/issues/34538. // Regression test for https://github.com/flutter/flutter/issues/34538.
testWidgets('RTL arabic correct caret placement after trailing whitespace', (WidgetTester tester) async { testWidgets('RTL arabic correct caret placement after trailing whitespace', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment