diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c45eb3..87e60db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## [3.0.0] * **Feat**: [401](https://github.com/SimformSolutionsPvtLtd/chatview/pull/401) - Add selection and copy options for text view. + Added text selection and copy functionality to chat bubbles. + Introduced the `onTextSelection` parameter in `FeatureActiveConfig` (default: false). When enabled, users can select and copy text with a long press. * **Breaking**: [318](https://github.com/SimformSolutionsPvtLtd/chatview/issues/318) Provide support for action item widgets on the chat text field with position options `leadingActions` and `trailingActions` in `TextFieldConfiguration`. Also, provide a way to add diff --git a/doc/documentation.md b/doc/documentation.md index db1a2f61..2cef8cd3 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -839,6 +839,61 @@ ChatView( enableOtherUserName: false, lastSeenAgoBuilderVisibility: false, receiptsBuilderVisibility: false, + enableTextSelection: true, + ), + // ... +) +``` + + +## Text Selection Config + +```dart +ChatView( + // ... + textSelectionConfig: TextSelectionConfig( + // Use platform-specific text selection controls (default is null for platform default) + selectionControls: null, + + // Focus node for managing text selection focus + focusNode: FocusNode(), + + // Callback triggered when text selection changes + onSelectionChanged: (SelectedContent? content) { + if (content != null) { + debugPrint('Selected text: ${content.plainText}'); + } + }, + + // Customize the context menu shown during text selection + contextMenuBuilder: (context, selectableRegionState) { + return AdaptiveTextSelectionToolbar( + anchors: selectableRegionState.contextMenuAnchors, + children: [ + // Add custom actions to the selection toolbar + TextSelectionToolbarTextButton( + padding: EdgeInsets.zero, + onPressed: () { + // Handle custom action (e.g., translate, search, etc.) + final selectedText = selectableRegionState.selectableRegion.getSelectedContent()?.plainText; + debugPrint('Custom action on: $selectedText'); + }, + child: const Text('Custom Action'), + ), + // Add more custom buttons as needed + ], + ); + }, + + // Configure the magnifier shown during text selection + magnifierConfiguration: const TextMagnifierConfiguration(), + + // Customize text selection theme (colors, handle size, etc.) + themeData: const TextSelectionThemeData( + cursorColor: Colors.blue, + selectionColor: Colors.blue, + selectionHandleColor: Colors.blue, + ), ), // ... ) diff --git a/lib/src/models/chat_bubble.dart b/lib/src/models/chat_bubble.dart index 87e2823d..c13d56ba 100644 --- a/lib/src/models/chat_bubble.dart +++ b/lib/src/models/chat_bubble.dart @@ -28,7 +28,6 @@ import 'config_models/text_selection_config.dart'; class ChatBubble { const ChatBubble({ - this.enableTextSelection = true, this.color, this.borderRadius, this.textStyle, @@ -74,11 +73,6 @@ class ChatBubble { /// Used for giving border of chat bubble. final Border? border; - /// Used to determine whether the text can be selected. - /// - /// Defaults to `true`. - final bool enableTextSelection; - /// Configuration for text selection behavior and appearance. final TextSelectionConfig? textSelectionConfig; } diff --git a/lib/src/models/config_models/feature_active_config.dart b/lib/src/models/config_models/feature_active_config.dart index 3fced058..d8ebc145 100644 --- a/lib/src/models/config_models/feature_active_config.dart +++ b/lib/src/models/config_models/feature_active_config.dart @@ -36,6 +36,7 @@ class FeatureActiveConfig { this.receiptsBuilderVisibility = true, this.enableOtherUserName = true, this.enableScrollToBottomButton = false, + this.enableTextSelection = false, }); /// Used for enable/disable swipe to reply. @@ -79,4 +80,9 @@ class FeatureActiveConfig { /// Used for enable/disable Scroll To Bottom Button. final bool enableScrollToBottomButton; + + /// Used to determine whether the text can be selected. + /// + /// Defaults to `false`. + final bool enableTextSelection; } diff --git a/lib/src/widgets/message_view.dart b/lib/src/widgets/message_view.dart index acaca5cb..6da99329 100644 --- a/lib/src/widgets/message_view.dart +++ b/lib/src/widgets/message_view.dart @@ -224,6 +224,7 @@ class _MessageViewState extends State messageReactionConfig: messageConfig?.messageReactionConfig, highlightColor: widget.highlightColor, highlightMessage: widget.shouldHighlight, + featureActiveConfig: chatViewIW?.featureActiveConfig, ); } else if (widget.message.messageType.isVoice) { return VoiceMessageView( diff --git a/lib/src/widgets/text_message_view.dart b/lib/src/widgets/text_message_view.dart index d82c8a37..a20aa52b 100644 --- a/lib/src/widgets/text_message_view.dart +++ b/lib/src/widgets/text_message_view.dart @@ -19,14 +19,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +import 'package:chatview/chatview.dart'; import 'package:chatview/src/widgets/custom_selection_area.dart'; -import 'package:chatview_utils/chatview_utils.dart'; import 'package:flutter/material.dart'; - import '../extensions/extensions.dart'; -import '../models/chat_bubble.dart'; -import '../models/config_models/link_preview_configuration.dart'; -import '../models/config_models/message_reaction_configuration.dart'; import '../utils/constants/constants.dart'; import 'link_preview.dart'; import 'reaction_widget.dart'; @@ -42,6 +38,7 @@ class TextMessageView extends StatelessWidget { this.messageReactionConfig, this.highlightMessage = false, this.highlightColor, + this.featureActiveConfig, }) : super(key: key); /// Represents current message is sent by current user. @@ -68,6 +65,8 @@ class TextMessageView extends StatelessWidget { /// Allow user to set color of highlighted message. final Color? highlightColor; + final FeatureActiveConfig? featureActiveConfig; + @override Widget build(BuildContext context) { final textTheme = Theme.of(context).textTheme; @@ -75,9 +74,7 @@ class TextMessageView extends StatelessWidget { final border = isMessageBySender ? outgoingChatBubbleConfig?.border : inComingChatBubbleConfig?.border; - final isSelectable = isMessageBySender - ? outgoingChatBubbleConfig?.enableTextSelection ?? false - : inComingChatBubbleConfig?.enableTextSelection ?? false; + final isSelectable = featureActiveConfig?.enableTextSelection ?? false; final textSelectionConfig = isMessageBySender ? outgoingChatBubbleConfig?.textSelectionConfig : inComingChatBubbleConfig?.textSelectionConfig;