diff --git a/simplistic_editor/lib/basic_text_input_client.dart b/simplistic_editor/lib/basic_text_input_client.dart index 4e24c86d2..63e7c898d 100644 --- a/simplistic_editor/lib/basic_text_input_client.dart +++ b/simplistic_editor/lib/basic_text_input_client.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'app_state.dart'; @@ -210,6 +211,10 @@ class BasicTextInputClientState extends State ), ); final TextStyle style = widget.style; + + _updateSizeAndTransform(); + _schedulePeriodicPostFrameCallbacks(); + _textInputConnection! ..setStyle( fontFamily: style.fontFamily, @@ -476,6 +481,54 @@ class BasicTextInputClientState extends State } } + // Sends the current composing rect to the iOS text input plugin via the text + // input channel. We need to keep sending the information even if no text is + // currently marked, as the information usually lags behind. The text input + // plugin needs to estimate the composing rect based on the latest caret rect, + // when the composing rect info didn't arrive in time. + void _updateComposingRectIfNeeded() { + final TextRange composingRange = _value.composing; + assert(mounted); + Rect? composingRect = + renderEditable.getRectForComposingRange(composingRange); + // Send the caret location instead if there's no marked text yet. + if (composingRect == null) { + assert(!composingRange.isValid || composingRange.isCollapsed); + final int offset = composingRange.isValid ? composingRange.start : 0; + composingRect = + renderEditable.getLocalRectForCaret(TextPosition(offset: offset)); + } + _textInputConnection!.setComposingRect(composingRect); + } + + void _updateCaretRectIfNeeded() { + final TextSelection? selection = renderEditable.selection; + if (selection == null || !selection.isValid || !selection.isCollapsed) { + return; + } + final TextPosition currentTextPosition = + TextPosition(offset: selection.baseOffset); + final Rect caretRect = + renderEditable.getLocalRectForCaret(currentTextPosition); + _textInputConnection!.setCaretRect(caretRect); + } + + void _updateSizeAndTransform() { + final Size size = renderEditable.size; + final Matrix4 transform = renderEditable.getTransformTo(null); + _textInputConnection!.setEditableSizeAndTransform(size, transform); + } + + void _schedulePeriodicPostFrameCallbacks([Duration? duration]) { + if (!_hasInputConnection) { + return; + } + _updateComposingRectIfNeeded(); + _updateCaretRectIfNeeded(); + SchedulerBinding.instance + .addPostFrameCallback(_schedulePeriodicPostFrameCallbacks); + } + /// [TextSelectionDelegate] method implementations. @override void bringIntoView(TextPosition position) {