我正在开发一个包含聊天功能的Flutter Web应用程序。
我想包含一个普通的输入功能,用户可以在其中输入文本并将其发送到聊天流中。如今,聊天应用程序的一个标准功能是在<ENTER>
上执行send
,并在<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
,暂时放弃多行功能。
感谢
这可以通过在TextField
中添加FocusNode
来实现。将焦点节点置于小部件的状态。
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,
)
值得一提的是,我能够想出一个合理的解决方案,我将在下面发布,以防有人自己遇到这种情况。
我将Textfield封装在一个键盘监听器中,当它看到是<Enter>
时,它会调用我的onSend
函数。我以前尝试过,但我想早些时候我错过了RawKeyEventDataWeb
的转换,这使我能够捕获isShiftPressed
,以便在不强制发送的情况下在<SHFT-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;
}
}