Skip to content
Open
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
5 changes: 5 additions & 0 deletions packages/flutter_chat_ui/lib/src/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class Chat extends StatefulWidget {
/// Callback triggered when a message is long-pressed.
final OnMessageLongPressCallback? onMessageLongPress;

/// Callback triggered when a message is right-clicked (secondary tapped).
final OnMessageSecondaryTapCallback? onMessageSecondaryTap;

/// Callback triggered when the attachment button in the composer is tapped.
final OnAttachmentTapCallback? onAttachmentTap;

Expand Down Expand Up @@ -79,6 +82,7 @@ class Chat extends StatefulWidget {
this.onMessageSend,
this.onMessageTap,
this.onMessageLongPress,
this.onMessageSecondaryTap,
this.onAttachmentTap,
this.backgroundColor,
this.decoration,
Expand Down Expand Up @@ -150,6 +154,7 @@ class _ChatState extends State<Chat> with WidgetsBindingObserver {
Provider.value(value: widget.onMessageSend),
Provider.value(value: widget.onMessageTap),
Provider.value(value: widget.onMessageLongPress),
Provider.value(value: widget.onMessageSecondaryTap),
Provider.value(value: widget.onAttachmentTap),
ChangeNotifierProvider(create: (_) => ComposerHeightNotifier()),
ChangeNotifierProvider(create: (_) => LoadMoreNotifier()),
Expand Down
11 changes: 10 additions & 1 deletion packages/flutter_chat_ui/lib/src/chat_message/chat_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class ChatMessage extends StatelessWidget {
final onMessageTap = context.read<OnMessageTapCallback?>();
final onMessageDoubleTap = context.read<OnMessageDoubleTapCallback?>();
final onMessageLongPress = context.read<OnMessageLongPressCallback?>();
final onMessageSecondaryTap =
context.read<OnMessageSecondaryTapCallback?>();
final isSentByMe = context.read<UserID>() == message.authorId;

final curvedAnimation = CurvedAnimation(
Expand All @@ -134,7 +136,7 @@ class ChatMessage extends StatelessWidget {

final resolvedPadding = padding ?? _resolveDefaultPadding(context);

final Widget messageWidget = Column(
final messageWidget = Column(
mainAxisSize: MainAxisSize.min,
children: [
if (headerWidget != null)
Expand Down Expand Up @@ -163,6 +165,13 @@ class ChatMessage extends StatelessWidget {
index: index,
details: details,
),
onSecondaryTapUp:
(details) => onMessageSecondaryTap?.call(
context,
message,
index: index,
details: details,
),
child: FadeTransition(
opacity: curvedAnimation,
child: SizeTransition(
Expand Down
10 changes: 9 additions & 1 deletion packages/flutter_chat_ui/lib/src/composer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ class Composer extends StatefulWidget {
/// and hide the button. Defaults to `false`.
final bool sendButtonHidden;

/// Whether to send messages on enter.
///
/// If `true`, typing enter will send the message, and typing shift+enter
/// will type enter. If `false`, typing shift+enter will send the message.
/// Defaults to `false`.
final bool sendOnEnter;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels incomplete. right now shift+enter will also send a message and enter will make a new line. if you want to send on enter - how user makes a new line?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable makes it so that the message sends on enter, and shift+enter only adds a new line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw the change to send on enter, but did not see the code for the new line itself - is it automatic?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used an XOR operator for it on line 222 of composer.dart, inside your existing logic for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It definitely works, I'm using it in my app right now.


/// Controls the behavior of the text input field after a message is sent.
/// Defaults to [InputClearMode.always].
final InputClearMode inputClearMode;
Expand Down Expand Up @@ -182,6 +189,7 @@ class Composer extends StatefulWidget {
this.allowEmptyMessage = false,
this.sendButtonDisabled = false,
this.sendButtonHidden = false,
this.sendOnEnter = false,
this.inputClearMode = InputClearMode.always,
this.contentInsertionConfiguration,
});
Expand Down Expand Up @@ -211,7 +219,7 @@ class _ComposerState extends State<Composer> {
// Check for Shift+Enter
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter &&
HardwareKeyboard.instance.isShiftPressed) {
widget.sendOnEnter ^ HardwareKeyboard.instance.isShiftPressed) {
_handleSubmitted(_textController.text);
return KeyEventResult.handled;
}
Expand Down
15 changes: 13 additions & 2 deletions packages/flutter_chat_ui/lib/src/utils/typedefs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ typedef OnMessageLongPressCallback =
void Function(
BuildContext context,
Message message, {
int index,
LongPressStartDetails details,
required int index,
required LongPressStartDetails details,
});

/// Callback signature for when a message is right-clicked (secondary tapped).
/// [context] is the BuildContext from the widget tree where the long press occurs.
/// Provides the long-pressed [message], its [index], and [LongPressStartDetails].
typedef OnMessageSecondaryTapCallback =
void Function(
BuildContext context,
Message message, {
required int index,
required TapUpDetails details,
});

/// Callback signature for when the user attempts to send a message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ class FlyerChatTextMessage extends StatelessWidget {
/// The widget to display on top of the message.
final Widget? topWidget;

/// Widget to display as text, overriding message text.
final Widget? customWidget;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove this - if you don't want markdown you just don't use this lib - default is SimpleTextMessage that does not use markdown. If you need a 3rd party lib - you should just copy the code and change it, and provide your class to the textMessageBuilder - this is a correct way to fully override.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is, I want all the features of the FlyerChatTextMessage, and the nice UI, but I need to be able to render HTML.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, just copy the code and use HTML :) I specifically created the builders pattern to avoid all the "customXXX" stuff that was everywhere in v1 - people would throw all sorts of things in there and then complain the package didn't work. Now, when someone needs something super custom, I prefer to give them full control and responsibility.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but thats a lot of code I need to copy, when I only want to change the display of one widget...
I guess I can just maintain a fork of this for my purposes.


/// Creates a widget to display a text message.
const FlyerChatTextMessage({
super.key,
Expand All @@ -149,6 +152,7 @@ class FlyerChatTextMessage extends StatelessWidget {
this.onLinkTap,
this.linkPreviewPosition = LinkPreviewPosition.bottom,
this.topWidget,
this.customWidget,
});

bool get _isOnlyEmoji => message.metadata?['isOnlyEmoji'] == true;
Expand Down Expand Up @@ -188,7 +192,7 @@ class FlyerChatTextMessage extends StatelessWidget {
)
: null;

final textContent = GptMarkdownTheme(
final textContent = customWidget ?? GptMarkdownTheme(
gptThemeData: GptMarkdownTheme.of(context),
child: GptMarkdown(
message.text,
Expand Down