Skip to content

Commit

Permalink
Fix: fix the delay of showOnScreen animation when keyboard comes up. (f…
Browse files Browse the repository at this point in the history
  • Loading branch information
luckysmg authored Mar 23, 2022
1 parent f1d8e30 commit dbbaf68
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 16 deletions.
40 changes: 24 additions & 16 deletions packages/flutter/lib/src/widgets/editable_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -2498,7 +2498,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien

bool _showCaretOnScreenScheduled = false;

void _scheduleShowCaretOnScreen() {
void _scheduleShowCaretOnScreen({required bool withAnimation}) {
if (_showCaretOnScreenScheduled) {
return;
}
Expand Down Expand Up @@ -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),
);
}
});
}

Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand Down
67 changes: 67 additions & 0 deletions packages/flutter/test/widgets/editable_text_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit dbbaf68

Please sign in to comment.