From 64e41475d9b4da457c93e6e7498aa439cb6b68f7 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Mon, 31 Oct 2022 12:22:56 +0100 Subject: [PATCH] fix(ui): use a predicate instead of a key --- packages/stream_chat_flutter/CHANGELOG.md | 2 +- .../message_input/stream_message_input.dart | 80 +++++++++---------- .../lib/src/utils/typedefs.dart | 5 ++ 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index e8047b76..82e1da48 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -2,7 +2,7 @@ ✅ Added -- Added `StreamMessageInput.sendMessageKey` and `StreamMessageInput.clearQuotedMessageKey` to customize the keys used to send and clear the quoted message. +- Added `StreamMessageInput.sendMessageKeyPredicate` and `StreamMessageInput.clearQuotedMessageKeyPredicate` to customize the keys used to send and clear the quoted message. ## 5.1.0 diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 4f83d080..0a7aa4d3 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -111,15 +111,16 @@ class StreamMessageInput extends StatefulWidget { this.enableMentionsOverlay = true, this.onQuotedMessageCleared, this.enableActionAnimation = true, - this.sendMessageKey = PhysicalKeyboardKey.enter, - this.clearQuotedMessageKey = PhysicalKeyboardKey.escape, + this.sendMessageKeyPredicate = _defaultSendMessageKeyPredicate, + this.clearQuotedMessageKeyPredicate = + _defaultClearQuotedMessageKeyPredicate, }); - /// The key used to send a message on web/desktop - final PhysicalKeyboardKey? sendMessageKey; + /// The predicate used to send a message on desktop/web + final RawKeyEventPredicate? sendMessageKeyPredicate; - /// The key used to send a message on web/desktop - final PhysicalKeyboardKey? clearQuotedMessageKey; + /// The predicate used to clear the quoted message on desktop/web + final RawKeyEventPredicate? clearQuotedMessageKeyPredicate; /// If true the message input will animate the actions while you type final bool enableActionAnimation; @@ -261,6 +262,18 @@ class StreamMessageInput extends StatefulWidget { static bool _defaultValidator(Message message) => message.text?.isNotEmpty == true || message.attachments.isNotEmpty; + static bool _defaultSendMessageKeyPredicate( + FocusNode node, + RawKeyEvent event, + ) => + event.logicalKey == LogicalKeyboardKey.enter; + + static bool _defaultClearQuotedMessageKeyPredicate( + FocusNode node, + RawKeyEvent event, + ) => + event.logicalKey == LogicalKeyboardKey.escape; + @override StreamMessageInputState createState() => StreamMessageInputState(); } @@ -760,44 +773,12 @@ class StreamMessageInputState extends State maxHeight: widget.maxHeight, child: PlatformWidgetBuilder( web: (context, child) => Focus( + onKey: _handleKeyPressed, child: child!, - onKeyEvent: (node, event) { - if (widget.sendMessageKey != null && - event.physicalKey == widget.sendMessageKey) { - sendMessage(); - return KeyEventResult.handled; - } else if (widget.clearQuotedMessageKey != null && - event.physicalKey == - widget.clearQuotedMessageKey) { - if (_hasQuotedMessage && - _effectiveController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - return KeyEventResult.handled; - } - - return KeyEventResult.ignored; - }, ), desktop: (context, child) => Focus( + onKey: _handleKeyPressed, child: child!, - onKeyEvent: (node, event) { - if (widget.sendMessageKey != null && - event.physicalKey == widget.sendMessageKey) { - sendMessage(); - return KeyEventResult.handled; - } else if (widget.clearQuotedMessageKey != null && - event.physicalKey == - widget.clearQuotedMessageKey) { - if (_hasQuotedMessage && - _effectiveController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - return KeyEventResult.handled; - } - - return KeyEventResult.ignored; - }, ), mobile: (context, child) => child, child: StreamMessageTextField( @@ -827,6 +808,25 @@ class StreamMessageInputState extends State ); } + KeyEventResult _handleKeyPressed( + FocusNode node, + RawKeyEvent event, + ) { + if (widget.sendMessageKeyPredicate != null && + widget.sendMessageKeyPredicate!(node, event)) { + sendMessage(); + return KeyEventResult.handled; + } else if (widget.clearQuotedMessageKeyPredicate != null && + widget.clearQuotedMessageKeyPredicate!(node, event)) { + if (_hasQuotedMessage && _effectiveController.text.isEmpty) { + widget.onQuotedMessageCleared?.call(); + } + return KeyEventResult.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..fda46b2d 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 raw key event +/// {@endtemplate} +typedef RawKeyEventPredicate = bool Function(FocusNode, RawKeyEvent); + /// {@template userItemBuilder} /// Builder used to create a custom [ListUserItem] from a [User] /// {@endtemplate}