标签:after rdk ack rev ado val sam test 不响应
由于Flutter的不断升级, 发现Zefyr插件在安卓平台无法正常的响应删除键了,后来测试桌面端也有此问题,在iOS中则正常。(我的Flutter是master分支 1.26.0-2.0.pre.173 )
动态调试,发现在 input.dart 中, 按下删除键时,updateEditingValue 函数都没有触发,但其它的一些输入则会触发,很怪异。更奇怪的是,在模拟器中调试状态 Zefyr 功能还一切正常,打包放到手机就不行了。
研究了 TextField 小部件的原码,发现在 RenderEditable 类中, 会在文本框获取焦点后,执行:
RawKeyboard.instance.addListener(_handleKeyEvent);
也就是给全局的键盘安装一个事件监听。在失去焦点的时候移除此事件处理。
来看看这个按键处理代码:
void _handleKeyEvent(RawKeyEvent keyEvent) { if (kIsWeb) { // On web platform, we should ignore the key because it‘s processed already. return; } if (keyEvent is! RawKeyDownEvent || onSelectionChanged == null) return; final Set<LogicalKeyboardKey> keysPressed = LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed); final LogicalKeyboardKey key = keyEvent.logicalKey; final bool isMacOS = keyEvent.data is RawKeyEventDataMacOs; if (!_nonModifierKeys.contains(key) || keysPressed.difference(isMacOS ? _macOsModifierKeys : _modifierKeys).length > 1 || keysPressed.difference(_interestingKeys).isNotEmpty) { // If the most recently pressed key isn‘t a non-modifier key, or more than // one non-modifier key is down, or keys other than the ones we‘re interested in // are pressed, just ignore the keypress. return; } // TODO(ianh): It seems to be entirely possible for the selection to be null here, but // all the keyboard handling functions assume it is not. assert(selection != null); final bool isWordModifierPressed = isMacOS ? keyEvent.isAltPressed : keyEvent.isControlPressed; final bool isLineModifierPressed = isMacOS ? keyEvent.isMetaPressed : keyEvent.isAltPressed; final bool isShortcutModifierPressed = isMacOS ? keyEvent.isMetaPressed : keyEvent.isControlPressed; if (_movementKeys.contains(key)) { _handleMovement(key, wordModifier: isWordModifierPressed, lineModifier: isLineModifierPressed, shift: keyEvent.isShiftPressed); } else if (isShortcutModifierPressed && _shortcutKeys.contains(key)) { // _handleShortcuts depends on being started in the same stack invocation // as the _handleKeyEvent method _handleShortcuts(key); } else if (key == LogicalKeyboardKey.delete) { _handleDelete(forward: true); } else if (key == LogicalKeyboardKey.backspace) { _handleDelete(forward: false); } }
可以看到,它处理了方向键和 delete, backspace 键,如果是 delete 键则向后删除, backspace 则向前删除,都是调用了 _handleDelete 函数。
void _handleDelete({ required bool forward }) { final TextSelection selection = textSelectionDelegate.textEditingValue.selection; final String text = textSelectionDelegate.textEditingValue.text; assert(_selection != null); if (_readOnly) { return; } String textBefore = selection.textBefore(text); String textAfter = selection.textAfter(text); int cursorPosition = math.min(selection.start, selection.end); // If not deleting a selection, delete the next/previous character. if (selection.isCollapsed) { if (!forward && textBefore.isNotEmpty) { final int characterBoundary = previousCharacter(textBefore.length, textBefore); textBefore = textBefore.substring(0, characterBoundary); cursorPosition = characterBoundary; } if (forward && textAfter.isNotEmpty) { final int deleteCount = nextCharacter(0, textAfter); textAfter = textAfter.substring(deleteCount); } } final TextSelection newSelection = TextSelection.collapsed(offset: cursorPosition); if (selection != newSelection) { _handleSelectionChange( newSelection, SelectionChangedCause.keyboard, ); } textSelectionDelegate.textEditingValue = TextEditingValue( text: textBefore + textAfter, selection: newSelection, ); }
这个 _handleDelete 函数主要就是根据删除方向,将选中的内容清掉并改变光标到正确的位置 ( _handleSelectionChange 函数 ),再将 textSelectionDelegate.textEditingValue 置为最新的值。
既然官方的 TextField 这样子来处理删除键,虽然我还不知道为什么之前不处理,过去也能正常,但现在将 Zefyr 插件出异常的安卓和win桌面端也这样处理一下应该就可以解决问题了。
lib/src/widgets/editable_text.dart 文件
import ‘dart:io‘; import ‘dart:math‘ as math; import ‘package:flutter/foundation.dart‘; ...... bool _listenerAttached = false; void _cancelSubscriptions() { _handleUpdateKeyEvent(false); _renderContext.removeListener(_handleRenderContextChange); widget.controller.removeListener(_handleLocalValueChange); _focusNode.removeListener(_handleFocusChange); _input.closeConnection(); _cursorTimer.stop(); } void _handleFocusChange() { _handleUpdateKeyEvent(); _input.openOrCloseConnection(_focusNode, widget.controller.plainTextEditingValue, widget.keyboardAppearance); _cursorTimer.startOrStop(_focusNode, selection); updateKeepAlive(); } void _handleUpdateKeyEvent([bool value]) { if (Platform.isAndroid || Platform.isWindows) {
// 因为只发现 android 和 windows 会有问题 if (_focusNode.hasFocus && (value == null || value == true)) { RawKeyboard.instance.addListener(_handleKeyEvent); _listenerAttached = true; } else if (_listenerAttached) { RawKeyboard.instance.removeListener(_handleKeyEvent); _listenerAttached = false; } } } // 主要增加的代码 void _handleKeyEvent(RawKeyEvent keyEvent) { if (kIsWeb) { // On web platform, we should ignore the key because it‘s processed already. return; } if (keyEvent is! RawKeyDownEvent) { return; } final keysPressed = LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed); final key = keyEvent.logicalKey; final isMacOS = keyEvent.data is RawKeyEventDataMacOs; if (!_nonModifierKeys.contains(key) || keysPressed.difference(isMacOS ? _macOsModifierKeys : _modifierKeys).length > 1 || keysPressed.difference(_interestingKeys).isNotEmpty) { // If the most recently pressed key isn‘t a non-modifier key, or more than // one non-modifier key is down, or keys other than the ones we‘re interested in // are pressed, just ignore the keypress. return; }
// 只管删除功能,方向键暂时不管,需要可以加上 if (key == LogicalKeyboardKey.delete) { _handleDelete(forward: true); } else if (key == LogicalKeyboardKey.backspace) { _handleDelete(forward: false); } } void _handleDelete({ @required bool forward }) { final selection = widget.controller.plainTextEditingValue.selection; assert(selection != null); final text = widget.controller.plainTextEditingValue.text; if (text.isEmpty) return; var textBefore = selection.textBefore(text); var textAfter = selection.textAfter(text); var cursorPosition = math.min(selection.start, selection.end); if (selection.isCollapsed) { if (!forward && cursorPosition > 0) { // ignore: invalid_use_of_visible_for_testing_member final characterBoundary = RenderEditable.previousCharacter(cursorPosition, textBefore); final newSelection = TextSelection.collapsed(offset: characterBoundary); widget.controller.replaceText(characterBoundary, cursorPosition - characterBoundary, ‘‘, selection: newSelection); } if (forward && textAfter.isNotEmpty) { // ignore: invalid_use_of_visible_for_testing_member final deleteCount = RenderEditable.nextCharacter(0, textAfter); final newSelection = TextSelection.collapsed(offset: cursorPosition); widget.controller.replaceText(cursorPosition, deleteCount, ‘‘, selection: newSelection); } } else { final newSelection = TextSelection.collapsed(offset: cursorPosition); widget.controller.replaceText(cursorPosition, math.max(selection.start, selection.end) - cursorPosition, ‘‘, selection: newSelection); } } static final Set<LogicalKeyboardKey> _movementKeys = <LogicalKeyboardKey>{ LogicalKeyboardKey.arrowRight, LogicalKeyboardKey.arrowLeft, LogicalKeyboardKey.arrowUp, LogicalKeyboardKey.arrowDown, }; static final Set<LogicalKeyboardKey> _shortcutKeys = <LogicalKeyboardKey>{ LogicalKeyboardKey.keyA, LogicalKeyboardKey.keyC, LogicalKeyboardKey.keyV, LogicalKeyboardKey.keyX, LogicalKeyboardKey.delete, LogicalKeyboardKey.backspace, }; static final Set<LogicalKeyboardKey> _nonModifierKeys = <LogicalKeyboardKey>{ ..._shortcutKeys, ..._movementKeys, }; static final Set<LogicalKeyboardKey> _modifierKeys = <LogicalKeyboardKey>{ LogicalKeyboardKey.shift, LogicalKeyboardKey.control, LogicalKeyboardKey.alt, }; static final Set<LogicalKeyboardKey> _macOsModifierKeys = <LogicalKeyboardKey>{ LogicalKeyboardKey.shift, LogicalKeyboardKey.meta, LogicalKeyboardKey.alt, }; static final Set<LogicalKeyboardKey> _interestingKeys = <LogicalKeyboardKey>{ ..._modifierKeys, ..._macOsModifierKeys, ..._nonModifierKeys, };
经测试,此问题成功解决。或许有一天,Flutter 底层就处理好了, 或许 Zefyr 作者改好了, 那就更完美了。
Zefyr 的坑什么时候能填完 ~~~~
[Flutter] Zefyr 富文本插件安卓和Windows桌面端不响应删除键问题
标签:after rdk ack rev ado val sam test 不响应
原文地址:https://www.cnblogs.com/yangyxd/p/14237407.html