Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [3.0.0] (unreleased)

* **Feat**: [401](https://github.com/SimformSolutionsPvtLtd/chatview/pull/401)
Add selection and copy options for text view.
* **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
Expand Down
11 changes: 11 additions & 0 deletions lib/src/models/chat_bubble.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import 'package:flutter/material.dart';

import 'config_models/link_preview_configuration.dart';
import 'config_models/receipts_widget_config.dart';
import 'config_models/text_selection_config.dart';

class ChatBubble {
const ChatBubble({
this.enableTextSelection = true,
this.color,
this.borderRadius,
this.textStyle,
Expand All @@ -37,6 +39,7 @@ class ChatBubble {
this.receiptsWidgetConfig,
this.onMessageRead,
this.border,
this.textSelectionConfig,
});

/// Used for giving color of chat bubble.
Expand Down Expand Up @@ -70,4 +73,12 @@ 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;
}
55 changes: 55 additions & 0 deletions lib/src/models/config_models/text_selection_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class TextSelectionConfig {
const TextSelectionConfig({
this.selectionControls,
this.focusNode,
this.onSelectionChanged,
this.contextMenuBuilder,
this.magnifierConfiguration,
this.themeData,
});

/// The delegate to build the selection handles and toolbar.
///
/// If it is null, the platform specific selection control is used.
final TextSelectionControls? selectionControls;

/// {@macro flutter.widgets.Focus.focusNode}
final FocusNode? focusNode;

/// The configuration for the magnifier in the selection region.
///
/// By default, builds a [CupertinoTextMagnifier] on iOS and [TextMagnifier]
/// on Android, and builds nothing on all other platforms. To suppress the
/// magnifier, consider passing [TextMagnifierConfiguration.disabled].
///
/// {@macro flutter.widgets.magnifier.intro}
final TextMagnifierConfiguration? magnifierConfiguration;

/// Called when the selected content changes.
final ValueChanged<SelectedContent?>? onSelectionChanged;

/// {@macro flutter.widgets.EditableText.contextMenuBuilder}
///
/// If not provided, will build a default menu based on the ambient
/// [ThemeData.platform].
///
/// {@tool dartpad}
/// This example shows how to build a custom context menu for any selected
/// content in a SelectionArea.
///
/// ** See code in examples/api/lib/material/context_menu/selectable_region_toolbar_builder.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [AdaptiveTextSelectionToolbar], which is built by default.
final SelectableRegionContextMenuBuilder? contextMenuBuilder;

/// Theme data for text selection.
///
/// If null, defaults to the theme's `TextSelectionThemeData`.
final TextSelectionThemeData? themeData;
}
1 change: 1 addition & 0 deletions lib/src/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export 'config_models/scroll_to_bottom_button_config.dart';
export 'config_models/send_message_configuration.dart';
export 'config_models/suggestion_list_config.dart';
export 'config_models/swipe_to_reply_configuration.dart';
export 'config_models/text_selection_config.dart';
export 'config_models/type_indicator_configuration.dart';
export 'config_models/voice_message_configuration.dart';
export 'overlay_action_widget.dart';
45 changes: 45 additions & 0 deletions lib/src/widgets/custom_selection_area.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:chatview/src/models/config_models/text_selection_config.dart';
import 'package:flutter/material.dart';

/// A convenience wrapper around [SelectionArea] that lets you locally
/// customize selection highlight, handle, and colors
/// without changing the global app [Theme].

class CustomSelectionArea extends StatelessWidget {
const CustomSelectionArea({
required this.child,
this.config,
super.key,
});

/// The subtree whose text becomes selectable.
final Widget child;

/// Configuration for text selection behavior and appearance.
final TextSelectionConfig? config;

@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(textSelectionTheme: config?.themeData),
child: SelectionArea(
focusNode: config?.focusNode,
selectionControls: config?.selectionControls,
contextMenuBuilder:
config?.contextMenuBuilder ?? _defaultContextMenuBuilder,
onSelectionChanged: config?.onSelectionChanged,
magnifierConfiguration: config?.magnifierConfiguration,
child: child,
),
);
}

static Widget _defaultContextMenuBuilder(
BuildContext context,
SelectableRegionState selectableRegionState,
) {
return AdaptiveTextSelectionToolbar.selectableRegion(
selectableRegionState: selectableRegionState,
);
}
}
29 changes: 21 additions & 8 deletions lib/src/widgets/text_message_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import 'package:chatview/src/widgets/custom_selection_area.dart';
import 'package:chatview_utils/chatview_utils.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -74,6 +75,20 @@ class TextMessageView extends StatelessWidget {
final border = isMessageBySender
? outgoingChatBubbleConfig?.border
: inComingChatBubbleConfig?.border;
final isSelectable = isMessageBySender
? outgoingChatBubbleConfig?.enableTextSelection ?? false
: inComingChatBubbleConfig?.enableTextSelection ?? false;
final textSelectionConfig = isMessageBySender
? outgoingChatBubbleConfig?.textSelectionConfig
: inComingChatBubbleConfig?.textSelectionConfig;
final baseWidget = Text(
textMessage,
style: _textStyle ??
textTheme.bodyMedium!.copyWith(
color: Colors.white,
fontSize: 16,
),
);
return Stack(
clipBehavior: Clip.none,
children: [
Expand All @@ -99,14 +114,12 @@ class TextMessageView extends StatelessWidget {
linkPreviewConfig: _linkPreviewConfig,
url: textMessage,
)
: Text(
textMessage,
style: _textStyle ??
textTheme.bodyMedium!.copyWith(
color: Colors.white,
fontSize: 16,
),
),
: isSelectable
? CustomSelectionArea(
config: textSelectionConfig,
child: baseWidget,
)
: baseWidget,
),
if (message.reaction.reactions.isNotEmpty)
ReactionWidget(
Expand Down