我正在开发一个包含聊天功能的 Flutter Web 应用程序。
我想包含一个普通的输入功能,用户可以在其中输入文本并将其发送到聊天流中。如今聊天应用程序的一个标准功能是在
send
上按 <ENTER>
并在 <SHIFT-ENTER>
上执行换行,或者是此功能的一些变体。
目前我一次只能实现这些功能中的一个。如果将
TextField
的 keyboardType
设置为 TextInputType.multiline
,则 <ENTER>
和 <SHIFT-ENTER>
始终执行换行,似乎没有办法覆盖此行为。
如果您的
TextField
是 TextInputType.text
,您可以捕获 <ENTER>
并发送,但尝试捕获 <SHIFT-ENTER>
添加换行符不起作用。我尝试通过 onKey
处理程序手动抓取按键并将 \n
插入到 controller.text
中,但看起来 TextInputType.text
根本不适合多行,所以它不能很好地发挥作用。
只是想知道是否有其他开发人员遇到过这个问题或提出任何合适的解决方案。理想情况下,解决方案也可以跨 android/ios 工作。对我来说,我决定使用
TextInputType.text
并暂时放弃多行功能。
谢谢
这可以通过在
FocusNode
上添加 TextField
来实现。将焦点节点置于小部件的状态中。
late final _focusNode = FocusNode(
onKey: (FocusNode node, RawKeyEvent evt) {
if (!evt.isShiftPressed && evt.logicalKey.keyLabel == 'Enter') {
if (evt is RawKeyDownEvent) {
_sendMessage();
}
return KeyEventResult.handled;
}
else {
return KeyEventResult.ignored;
}
},
);
在您的
build
函数中,创建 TextField
时添加焦点。
TextField(
autofocus: true,
controller: _textController,
focusNode: _focusNode,
)
对于它的价值,我能够制定一个合理的解决方案,我将在下面发布,以防有人自己遇到这个问题。
我将文本字段包装在键盘侦听器中,当它看到
onSend
时,它会调用我的 <Enter>
函数。我之前尝试过,但我想之前我错过了 RawKeyEventDataWeb
的转换,这使我能够捕获 isShiftPressed
以便在 <SHFT-ENTER>
上添加新行,而无需强制发送。不幸的是,我不得不添加一些 hacky 代码来删除按 Enter 键时添加的 \n
,但对于功能性+现代消息传递来说,这是一个很小的代价。
RawKeyboardListener(
focusNode: focusNode,
onKey: handleKeyPress,
child: TextField(
controller: messageController,
minLines: 1,
maxLines: null,
textInputAction: TextInputAction.done,
style: normalTextStyle,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
isDense: true,
hintText: 'Type a message',
hintStyle: TextStyle(
fontSize: 16,
color: Color(0xFF474749),
),
border: InputBorder.none,
),
),
)
void handleKeyPress(event) {
if (event is RawKeyUpEvent && event.data is RawKeyEventDataWeb) {
var data = event.data as RawKeyEventDataWeb;
if (data.code == "Enter" && !event.isShiftPressed) {
final val = messageController.value;
final messageWithoutNewLine =
messageController.text.substring(0, val.selection.start - 1) +
messageController.text.substring(val.selection.start);
messageController.value = TextEditingValue(
text: messageWithoutNewLine,
selection: TextSelection.fromPosition(
TextPosition(offset: messageWithoutNewLine.length),
),
);
_onSend();
}
}
}
这就是我在
TextField
中使用的支持输入换行符的方法。
class TextInputsWidget extends StatelessWidget {
final TextEditingController chatTextFieldController = TextEditingController();
late final _focusNode = FocusNode(
onKey: _handleKeyPress,
);
KeyEventResult _handleKeyPress(FocusNode focusNode, RawKeyEvent event) {
// handles submit on enter
if (event.isKeyPressed(LogicalKeyboardKey.enter) && !event.isShiftPressed) {
_sendMessage();
// handled means that the event will not propagate
return KeyEventResult.handled;
}
// ignore every other keyboard event including SHIFT+ENTER
return KeyEventResult.ignored;
}
void _sendMessage() {
if (chatTextFieldController.text.trim().isNotEmpty) {
// Do something with your input text
print(chatTextFieldController.text.trim());
// bring focus back to the input field
Future.delayed(Duration.zero, () {
_focusNode.requestFocus();
chatTextFieldController.clear();
});
}
}
@override
Widget build(BuildContext context) {
return Container(
child: TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
textInputAction: TextInputAction.newline,
autofocus: true,
focusNode: _focusNode,
controller: chatTextFieldController,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.fromLTRB(8, 0, 0, 0),
hintText: "Enter your message here",
hintStyle: TextStyle(color: Colors.black54),
),
),
);
}
}
主要有3个关键变化
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
FocusNode
可以监听键盘事件禁用输入的
Enter
键并在没有按下 ctrl
键时发送它的最佳方法是直接通过输入上的 focusNode,这样您就不必删除额外的新行。
class _InputTextState extends State<InputText> {
late final _focusNode = FocusNode(onKey: handleKeyPress);
@override
Widget build(BuildContext context) {
return TextField(
focusNode: _focusNode,
);
}
KeyEventResult handleKeyPress(FocusNode focusNode, RawKeyEvent event) {
// handles submit on enter
if (kIsWeb &&
event.isKeyPressed(LogicalKeyboardKey.enter) &&
!event.isControlPressed &&
!event.isShiftPressed) {
widget.onSubmit();
// handled means that the event will not propagate
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
}
如果您想使用
onKeyEvent
而不是 onKey
,这是另一种解决方案。由于根据文档,onKey
将在某一时刻被弃用:
This is a legacy API based on [RawKeyEvent] and will be deprecated in the future. Prefer [onKeyEvent] instead.
// In State class
FocusNode? textFieldNode;
@override
void initState() {
textFieldNode = FocusNode(
onKeyEvent: (node, event) {
final enterPressedWithoutShift = event is KeyDownEvent &&
event.physicalKey == PhysicalKeyboardKey.enter &&
!HardwareKeyboard.instance.physicalKeysPressed.any(
(key) => <PhysicalKeyboardKey>{
PhysicalKeyboardKey.shiftLeft,
PhysicalKeyboardKey.shiftRight,
}.contains(key),
);
if (enterPressedWithoutShift) {
// Submit stuff
return KeyEventResult.handled;
} else if (event is KeyRepeatEvent) {
// Disable holding enter
return KeyEventResult.handled;
} else {
return KeyEventResult.ignored;
}
},
);
super.initState();
}
在小部件中:
TextField(
focusNode: textFieldNode,
)