diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 84dafba6..a0cd21e0 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -10,6 +10,7 @@ - Now it is possible to customize the max lines of the title of a url attachment. Before it was always 1 line. - Added `attachmentActionsModalBuilder` parameter to `StreamMessageWidget` that allows to customize `AttachmentActionsModal`. +- Added `StreamMessageInput.sendMessageKeyPredicate` and `StreamMessageInput.clearQuotedMessageKeyPredicate` to customize the keys used to send and clear the quoted message. 🔄 Changed diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart index 9d3470d6..0487bd43 100644 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart +++ b/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart' show Theme; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; /// A generic widget builder function. @@ -30,7 +30,7 @@ abstract class PlatformWidgetBase message.text?.isNotEmpty == true || message.attachments.isNotEmpty; + static bool _defaultSendMessageKeyPredicate( + FocusNode node, + KeyEvent event, + ) { + // On desktop/web, send the message when the user presses the enter key. + return event is KeyUpEvent && event.logicalKey == LogicalKeyboardKey.enter; + } + + static bool _defaultClearQuotedMessageKeyPredicate( + FocusNode node, + KeyEvent event, + ) { + // On desktop/web, clear the quoted message when the user presses the escape key. + return event is KeyUpEvent && event.logicalKey == LogicalKeyboardKey.escape; + } + @override StreamMessageInputState createState() => StreamMessageInputState(); } @@ -509,9 +535,6 @@ class StreamMessageInputState extends State : CrossFadeState.showSecond, ), ), - // PlatformWidgetBuilder( - // mobile: (context, child) => _buildFilePickerSection(), - // ), ], ), ), @@ -753,24 +776,12 @@ class StreamMessageInputState extends State LimitedBox( maxHeight: widget.maxHeight, child: PlatformWidgetBuilder( - web: (context, child) => KeyboardShortcutRunner( - onEnterKeypress: sendMessage, - onEscapeKeypress: () { - if (_hasQuotedMessage && - _effectiveController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - }, + web: (context, child) => Focus( + onKeyEvent: _handleKeyPressed, child: child!, ), - desktop: (context, child) => KeyboardShortcutRunner( - onEnterKeypress: sendMessage, - onEscapeKeypress: () { - if (_hasQuotedMessage && - _effectiveController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - }, + desktop: (context, child) => Focus( + onKeyEvent: _handleKeyPressed, child: child!, ), mobile: (context, child) => child, @@ -801,6 +812,25 @@ class StreamMessageInputState extends State ); } + KeyEventResult _handleKeyPressed(FocusNode node, KeyEvent event) { + // Check for send message key. + if (widget.sendMessageKeyPredicate(node, event)) { + sendMessage(); + return KeyEventResult.handled; + } + + // Check for clear quoted message key. + if (widget.clearQuotedMessageKeyPredicate(node, event)) { + if (_hasQuotedMessage && _effectiveController.text.isEmpty) { + widget.onQuotedMessageCleared?.call(); + } + return KeyEventResult.handled; + } + + // Return ignored to allow other key events to be handled. + return KeyEventResult.ignored; + } + InputDecoration _getInputDecoration(BuildContext context) { final passedDecoration = _messageInputTheme.inputDecoration; return InputDecoration( diff --git a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart index b6e1af43..6cc581bf 100644 --- a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart +++ b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart @@ -340,6 +340,11 @@ typedef DownloadedPathCallback = void Function(String? path); /// {@endtemplate} typedef UserTapCallback = void Function(User, Widget?); +/// {@template rawKeyEventPredicate} +/// Callback called to react to a key event +/// {@endtemplate} +typedef KeyEventPredicate = bool Function(FocusNode, KeyEvent); + /// {@template userItemBuilder} /// Builder used to create a custom [ListUserItem] from a [User] /// {@endtemplate}