Unverified Commit 533a5dde authored by Tomasz Gucio's avatar Tomasz Gucio Committed by GitHub

Call bringIntoView after RenderEditable updates on paste (#98604)

parent edc16121
...@@ -1689,7 +1689,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1689,7 +1689,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
Clipboard.setData(ClipboardData(text: selection.textInside(text))); Clipboard.setData(ClipboardData(text: selection.textInside(text)));
_replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause)); _replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause));
if (cause == SelectionChangedCause.toolbar) { if (cause == SelectionChangedCause.toolbar) {
bringIntoView(textEditingValue.selection.extent); // Schedule a call to bringIntoView() after renderEditable updates.
SchedulerBinding.instance.addPostFrameCallback((_) {
bringIntoView(textEditingValue.selection.extent);
});
hideToolbar(); hideToolbar();
} }
_clipboardStatus?.update(); _clipboardStatus?.update();
...@@ -1715,7 +1718,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -1715,7 +1718,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_replaceText(ReplaceTextIntent(textEditingValue, data.text!, selection, cause)); _replaceText(ReplaceTextIntent(textEditingValue, data.text!, selection, cause));
if (cause == SelectionChangedCause.toolbar) { if (cause == SelectionChangedCause.toolbar) {
bringIntoView(textEditingValue.selection.extent); // Schedule a call to bringIntoView() after renderEditable updates.
SchedulerBinding.instance.addPostFrameCallback((_) {
bringIntoView(textEditingValue.selection.extent);
});
hideToolbar(); hideToolbar();
} }
} }
...@@ -3095,10 +3101,20 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien ...@@ -3095,10 +3101,20 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
} }
void _replaceText(ReplaceTextIntent intent) { void _replaceText(ReplaceTextIntent intent) {
userUpdateTextEditingValue( final TextEditingValue oldValue = _value;
intent.currentTextEditingValue.replaced(intent.replacementRange, intent.replacementText), final TextEditingValue newValue = intent.currentTextEditingValue.replaced(
intent.cause, intent.replacementRange,
intent.replacementText,
); );
userUpdateTextEditingValue(newValue, intent.cause);
// If there's no change in text and selection (e.g. when selecting and
// pasting identical text), the widget won't be rebuilt on value update.
// Handle this by calling _didChangeTextEditingValue() so caret and scroll
// updates can happen.
if (newValue == oldValue) {
_didChangeTextEditingValue();
}
} }
late final Action<ReplaceTextIntent> _replaceTextAction = CallbackAction<ReplaceTextIntent>(onInvoke: _replaceText); late final Action<ReplaceTextIntent> _replaceTextAction = CallbackAction<ReplaceTextIntent>(onInvoke: _replaceText);
......
...@@ -10948,12 +10948,12 @@ void main() { ...@@ -10948,12 +10948,12 @@ void main() {
// Paste // Paste
await resetSelectionAndScrollOffset(); await resetSelectionAndScrollOffset();
textSelectionDelegate.pasteText(SelectionChangedCause.keyboard); await textSelectionDelegate.pasteText(SelectionChangedCause.keyboard);
await tester.pump(); await tester.pump();
expect(scrollController.offset, maxScrollExtent); expect(scrollController.offset, maxScrollExtent);
await resetSelectionAndScrollOffset(); await resetSelectionAndScrollOffset();
textSelectionDelegate.pasteText(SelectionChangedCause.toolbar); await textSelectionDelegate.pasteText(SelectionChangedCause.toolbar);
await tester.pump(); await tester.pump();
expect(scrollController.offset.roundToDouble(), 0.0); expect(scrollController.offset.roundToDouble(), 0.0);
...@@ -10980,6 +10980,50 @@ void main() { ...@@ -10980,6 +10980,50 @@ void main() {
expect(scrollController.offset.roundToDouble(), 0.0); expect(scrollController.offset.roundToDouble(), 0.0);
}); });
testWidgets('Should not scroll on paste if caret already visible', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/96658.
final ScrollController scrollController = ScrollController();
final TextEditingController controller = TextEditingController(
text: 'Lorem ipsum please paste here: \n${".\n" * 50}',
);
final FocusNode focusNode = FocusNode();
await tester.pumpWidget(
MaterialApp(
home: Center(
child: SizedBox(
height: 600.0,
width: 600.0,
child: EditableText(
controller: controller,
scrollController: scrollController,
focusNode: focusNode,
maxLines: null,
style: const TextStyle(fontSize: 36.0),
backgroundCursorColor: Colors.grey,
cursorColor: cursorColor,
),
),
),
)
);
await Clipboard.setData(const ClipboardData(text: 'Fairly long text to be pasted'));
focusNode.requestFocus();
final EditableTextState state =
tester.state<EditableTextState>(find.byType(EditableText));
expect(scrollController.offset, 0.0);
controller.selection = const TextSelection.collapsed(offset: 31);
await state.pasteText(SelectionChangedCause.toolbar);
await tester.pumpAndSettle();
// No scroll should happen as the caret is in the viewport all the time.
expect(scrollController.offset, 0.0);
});
testWidgets('Autofill enabled by default', (WidgetTester tester) async { testWidgets('Autofill enabled by default', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode(); final FocusNode focusNode = FocusNode();
await tester.pumpWidget( await tester.pumpWidget(
......
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