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
// to make sure the user can see the changes they just made. Programmatical
// changes to `textEditingValue` do not trigger the behavior even if the
// text field is focused.
_scheduleShowCaretOnScreen();
_scheduleShowCaretOnScreen(withAnimation: true);
if (_hasInputConnection) {
// To keep the cursor from blinking while typing, we want to restart the
// cursor timer every time a new character is typed.
......@@ -2498,7 +2498,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
bool _showCaretOnScreenScheduled = false;
void _scheduleShowCaretOnScreen() {
void _scheduleShowCaretOnScreen({required bool withAnimation}) {
if (_showCaretOnScreenScheduled) {
return;
}
......@@ -2538,17 +2538,23 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final RevealedOffset targetOffset = _getOffsetToRevealCaret(_currentCaretRect!);
_scrollController.animateTo(
targetOffset.offset,
duration: _caretAnimationDuration,
curve: _caretAnimationCurve,
);
renderEditable.showOnScreen(
rect: caretPadding.inflateRect(targetOffset.rect),
duration: _caretAnimationDuration,
curve: _caretAnimationCurve,
);
if (withAnimation) {
_scrollController.animateTo(
targetOffset.offset,
duration: _caretAnimationDuration,
curve: _caretAnimationCurve,
);
renderEditable.showOnScreen(
rect: caretPadding.inflateRect(targetOffset.rect),
duration: _caretAnimationDuration,
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
_selectionOverlay?.updateForScroll();
});
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;
......@@ -2745,7 +2753,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
WidgetsBinding.instance.addObserver(this);
_lastBottomViewInset = WidgetsBinding.instance.window.viewInsets.bottom;
if (!widget.readOnly) {
_scheduleShowCaretOnScreen();
_scheduleShowCaretOnScreen(withAnimation: true);
}
if (!_value.selection.isValid) {
// 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
? _value.selection != value.selection
: _value != value;
if (shouldShowCaret) {
_scheduleShowCaretOnScreen();
_scheduleShowCaretOnScreen(withAnimation: true);
}
_formatAndSetValue(value, cause, userInteraction: true);
}
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
......@@ -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;
final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Node');
final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'EditableText Scope Node');
......@@ -107,6 +129,51 @@ void main() {
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.
testWidgets('RTL arabic correct caret placement after trailing whitespace', (WidgetTester tester) async {
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