From 9dce49eb78cc361bbb534b265160ad512693e300 Mon Sep 17 00:00:00 2001 From: Misaka Date: Fri, 15 May 2026 22:09:31 +0800 Subject: [PATCH] Sync full Android text after composition settles --- lib/main.dart | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fbf65b8..4319585 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -42,7 +43,8 @@ class _InputSyncPageState extends State { final inputFocusNode = FocusNode(); WebSocketChannel? channel; - String lastText = ''; + Timer? syncDebounce; + String lastSyncedText = ''; bool connected = false; @override @@ -54,6 +56,7 @@ class _InputSyncPageState extends State { @override void dispose() { + syncDebounce?.cancel(); channel?.sink.close(); hostController.dispose(); portController.dispose(); @@ -85,10 +88,12 @@ class _InputSyncPageState extends State { setState(() { connected = true; }); + _sendCurrentText(force: true); inputFocusNode.requestFocus(); } void _disconnect() { + syncDebounce?.cancel(); channel?.sink.close(); channel = null; setState(() { @@ -97,23 +102,40 @@ class _InputSyncPageState extends State { } void _syncTextChange() { - final currentText = inputController.text; - if (!connected || channel == null || currentText == lastText) { - lastText = currentText; + if (!connected || channel == null) { return; } - if (currentText.length > lastText.length && - currentText.startsWith(lastText)) { - _send('insertText', text: currentText.substring(lastText.length)); - } else if (lastText.length == currentText.length + 1 && - lastText.startsWith(currentText)) { - _send('backspace'); - } else { - _send('insertText', text: currentText); + final value = inputController.value; + if (value.composing.isValid) { + return; } - lastText = currentText; + final currentText = value.text; + if (currentText == lastSyncedText) { + return; + } + + syncDebounce?.cancel(); + syncDebounce = Timer(const Duration(milliseconds: 800), _sendCurrentText); + } + + void _sendCurrentText({bool force = false}) { + if (!connected || channel == null) { + return; + } + + final latestValue = inputController.value; + if (latestValue.composing.isValid) { + return; + } + + if (!force && latestValue.text == lastSyncedText) { + return; + } + + lastSyncedText = latestValue.text; + _send('replaceAll', text: latestValue.text); } void _send(String action, {String? text}) {