Skip to content

Commit 8227ffa

Browse files
japanshah-simformvasu-nageshri
authored andcommitted
feat: Enhance edit message functionality with updated callback and integration examples
1 parent 289c35f commit 8227ffa

8 files changed

Lines changed: 50 additions & 78 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Flutter applications with [Flexible Backend Integration][chatViewConnect].
3939
- Custom message types
4040
- Typing indicators
4141
- Reply suggestions
42+
- Message editing support
4243
- Message status indicators (sent, delivered, read)
4344
- Highly customizable UI components
4445
- Plug-and-play backend support using [chatview_connect][chatViewConnect]

doc/documentation.md

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -846,14 +846,14 @@ ChatView(
846846
ChatView(
847847
// ...
848848
featureActiveConfig: FeatureActiveConfig(
849-
enableSwipeToReply: true,
850-
enableSwipeToSeeTime: false,
851-
enablePagination: true,
852-
enableOtherUserName: false,
853-
lastSeenAgoBuilderVisibility: false,
854-
receiptsBuilderVisibility: false,
855-
enableTextSelection: true,
856-
enableEditMessage: true, // Enable the Edit Message feature (default: true)
849+
enableSwipeToReply: true, // Enable swipe to reply (default: true)
850+
enableSwipeToSeeTime: false, // Enable swipe to see message time (default: true)
851+
enablePagination: true, // Enable pagination (default: false)
852+
enableOtherUserName: false, // Show other user's name above bubble (default: true)
853+
lastSeenAgoBuilderVisibility: false, // Show last seen ago label (default: true)
854+
receiptsBuilderVisibility: false, // Show message receipts (default: true)
855+
enableTextSelection: true, // Enable text selection in bubbles (default: false)
856+
enableMessageEditing: true, // Enable the edit message feature (default: false)
857857
),
858858
// ...
859859
)
@@ -869,43 +869,29 @@ ChatView supports editing previously sent text messages, similar to WhatsApp and
869869
2. Tap **Edit** in the reply pop-up.
870870
3. The original text is pre-filled into the input field with an *Editing* indicator above it.
871871
4. Modify the text and press Send.
872-
5. Your `onEditTap` callback is called with the **original `Message`** and the **new text**.
873-
6. Messages that have been edited display a subtle *Edited* label below the bubble.
872+
5. Your `onEditTap` callback is called with the **original `Message`** (`oldMessage`) and the **updated `Message`** (`newMessage`).
873+
6. Messages that have been edited display a subtle _Edited_ label below the bubble.
874874

875875
### Basic Integration
876876

877877
```dart
878878
ChatView(
879879
// ...
880-
onEditTap: (Message originalMessage, String newText) {
881-
// Update the message in your data source / backend.
882-
// Set `updateAt` on the message to trigger the "Edited" label in the UI.
883-
final updatedMessage = Message(
884-
id: originalMessage.id,
885-
message: newText,
886-
createdAt: originalMessage.createdAt,
887-
sentBy: originalMessage.sentBy,
888-
updateAt: DateTime.now(), // marks the message as edited
889-
replyMessage: originalMessage.replyMessage,
890-
messageType: originalMessage.messageType,
891-
);
892-
893-
chatController.updateMessage(updatedMessage); // depends on your controller API
894-
},
895-
replyPopupConfig: ReplyPopupConfiguration(
896-
// Optional: react to the edit tap in the popup before the editing UI opens.
897-
onEditTap: (message) => debugPrint('Editing: ${message.id}'),
898-
),
880+
// updatedMessage is a copy of oldMessage with the new text already applied.
881+
// You have access to both objects so you can do further transformations —
882+
// for example, update the timestamp, change the message type, or sync with a backend.
883+
onEditTap: (message, updatedMessage) =>
884+
_chatController.updateMessage(messageId: message.id, newMessage: updatedMessage),
899885
// ...
900886
)
901887
```
902888

903-
### Disabling the Feature
889+
### Enabeling the Feature
904890

905891
```dart
906892
ChatView(
907893
featureActiveConfig: const FeatureActiveConfig(
908-
enableEditMessage: false, // hides the Edit button entirely
894+
enableEditMessage: true,
909895
),
910896
)
911897
```
@@ -927,18 +913,6 @@ PackageStrings.addLocaleObject(
927913
PackageStrings.setLocale('es');
928914
```
929915

930-
### Static Helper
931-
932-
Use `ChatView.getEditingMessage(context)` to read the message currently being edited
933-
from a widget that is a descendant of `ChatView`:
934-
935-
```dart
936-
final editingMsg = ChatView.getEditingMessage(context);
937-
if (editingMsg != null) {
938-
// User is in edit mode for editingMsg.
939-
}
940-
```
941-
942916
### Customizing the Edit Indicator Label
943917

944918
When a user enters edit mode, an indicator bar is displayed above the text field showing a label

example/lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ class _ExampleOneChatScreenState extends State<ExampleOneChatScreen> {
426426
return Scaffold(
427427
body: SafeArea(
428428
child: ChatView(
429+
onEditTap: (message, updatedMessage) => _chatController.updateMessage(
430+
messageId: message.id, newMessage: updatedMessage),
429431
chatController: _chatController,
430432
onSendTap: _onSendTap,
431433
isLastPage: () => _isTopPaginationCalled && _isBottomPaginationCalled,
@@ -495,6 +497,7 @@ class _ExampleOneChatScreenState extends State<ExampleOneChatScreen> {
495497
enableScrollToBottomButton: true,
496498
enableOtherUserProfileAvatar: true,
497499
enablePagination: true,
500+
enableMessageEditing: true,
498501
),
499502
scrollToBottomButtonConfig: const ScrollToBottomButtonConfig(
500503
padding: EdgeInsets.only(bottom: 8, right: 12),

lib/src/models/config_models/feature_active_config.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class FeatureActiveConfig {
3737
this.enableOtherUserName = true,
3838
this.enableScrollToBottomButton = false,
3939
this.enableTextSelection = false,
40-
this.enableMessageEditing = true,
40+
this.enableMessageEditing = false,
4141
});
4242

4343
/// Used for enable/disable swipe to reply.
@@ -93,6 +93,6 @@ class FeatureActiveConfig {
9393
/// messages sent by the current user, allowing them to modify the
9494
/// message text in-place.
9595
///
96-
/// Defaults to `true`.
96+
/// Defaults to `false`.
9797
final bool enableMessageEditing;
9898
}

lib/src/values/typedefs.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ typedef ChatBubbleLongPressCallback = void Function(
9898
);
9999

100100
/// Callback invoked when the user confirms an edit.
101-
/// [message] is the original message being edited.
102-
/// [updatedMessage] is the updated text content.
101+
/// [message] is the original message before editing.
102+
/// [updatedMessage] is the updated message object with new content.
103103
typedef EditMessageCallback = void Function(
104104
Message message,
105-
String updatedMessage,
105+
Message updatedMessage,
106106
);
107107
typedef ChatTextFieldViewBuilderCallback<T> = Widget Function(
108108
BuildContext context,

lib/src/widgets/chat_bubble_widget.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ class _ChatBubbleWidgetState extends State<ChatBubbleWidget> {
274274
onTap: () => widget.onReplyTap
275275
?.call(widget.message.replyMessage.messageId),
276276
),
277-
if (widget.message.updateAt != null)
277+
if (widget.message.updatedAt != null)
278278
Padding(
279279
padding: EdgeInsets.only(
280280
left: isMessageBySender ? 0 : 8,

lib/src/widgets/chat_view.dart

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,6 @@ class ChatView extends StatefulWidget {
179179
return state?._sendMessageKey.currentState?.replyMessage;
180180
}
181181

182-
/// Returns the [Message] currently being edited, or `null` if not in edit
183-
/// mode. Useful when you manage the chat state outside of [ChatView].
184-
static Message? getEditingMessage(BuildContext context) {
185-
final state = context.findAncestorStateOfType<_ChatViewState>();
186-
187-
assert(
188-
state != null,
189-
'ChatViewState not found. Make sure to use correct context that contains the ChatViewState',
190-
);
191-
192-
return state?._sendMessageKey.currentState?.isEditMode == true
193-
? state?._sendMessageKey.currentState?.editMessage
194-
: null;
195-
}
196-
197182
@override
198183
State<ChatView> createState() => _ChatViewState();
199184
}
@@ -323,10 +308,12 @@ class _ChatViewState extends State<ChatView>
323308
// sendMessageBuilder is provided, the caller is
324309
// responsible for handling edit via
325310
// ReplyPopupConfiguration.onEditTap instead.
326-
assignEditMessage: widget.sendMessageBuilder == null
327-
? (message) => _sendMessageKey.currentState
328-
?.assignEditMessage(message)
329-
: null,
311+
assignEditMessage:
312+
widget.sendMessageBuilder == null
313+
? (message) => _sendMessageKey
314+
.currentState
315+
?.assignEditMessage(message)
316+
: null,
330317
textFieldConfig:
331318
widget.sendMessageConfig.textFieldConfig,
332319
),

lib/src/widgets/send_message_widget.dart

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class SendMessageWidget extends StatefulWidget {
5454
final SendMessageConfiguration sendMessageConfig;
5555

5656
/// Callback invoked when the user confirms an edit.
57-
/// Receives the original [Message] and the updated text.
57+
/// Receives the original [Message] and the updated [Message] with new content.
5858
final EditMessageCallback? onEditTap;
5959

6060
/// Allow user to set custom text field.
@@ -85,13 +85,13 @@ class SendMessageWidgetState extends State<SendMessageWidget> {
8585
ReplyMessage _replyMessage = const ReplyMessage();
8686

8787
/// The message currently being edited, or `null` if not in edit mode.
88-
Message? _editMessage;
88+
Message? _currentlyEditingMessage;
8989

9090
/// Whether the text field is currently in edit mode.
91-
bool get isEditMode => _editMessage != null;
91+
bool get isEditMode => _currentlyEditingMessage != null;
9292

9393
/// Public read-only access to the message currently being edited.
94-
Message? get editMessage => _editMessage;
94+
Message? get currentlyEditingMessage => _currentlyEditingMessage;
9595

9696
ReplyMessage get replyMessage => _replyMessage;
9797

@@ -188,10 +188,11 @@ class SendMessageWidgetState extends State<SendMessageWidget> {
188188
onChange: (value) {
189189
// When the user cancels via the X button
190190
// on EditMessageView, clear the text field.
191-
if (value == null && _editMessage != null) {
191+
if (value == null &&
192+
_currentlyEditingMessage != null) {
192193
_textEditingController.clear();
193194
}
194-
_editMessage = value;
195+
_currentlyEditingMessage = value;
195196
},
196197
),
197198
if (widget
@@ -270,7 +271,13 @@ class SendMessageWidgetState extends State<SendMessageWidget> {
270271
// Only confirm the edit if a handler is registered; otherwise keep edit
271272
// mode active so the user's text is not silently discarded.
272273
if (widget.onEditTap == null) return;
273-
widget.onEditTap!.call(_editMessage!, messageText);
274+
widget.onEditTap!.call(
275+
_currentlyEditingMessage!,
276+
_currentlyEditingMessage!.copyWith(
277+
message: messageText,
278+
updatedAt: DateTime.now(),
279+
),
280+
);
274281
_closeEditMode();
275282
return;
276283
}
@@ -301,7 +308,7 @@ class SendMessageWidgetState extends State<SendMessageWidget> {
301308
const ReplyMessage();
302309
}
303310

304-
_editMessage = message;
311+
_currentlyEditingMessage = message;
305312
_textEditingController.text = message.message;
306313
// Place cursor at end.
307314
_textEditingController.selection = TextSelection.fromPosition(
@@ -317,12 +324,12 @@ class SendMessageWidgetState extends State<SendMessageWidget> {
317324
}
318325

319326
void _closeEditMode() {
320-
if (_editMessage == null) return; // Nothing to close.
327+
if (_currentlyEditingMessage == null) return; // Nothing to close.
321328
// Clear the text field when cancelling an edit.
322329
_textEditingController.clear();
323330
if (_editMessageViewKey.currentState == null) {
324331
setState(() {
325-
_editMessage = null;
332+
_currentlyEditingMessage = null;
326333
});
327334
} else {
328335
_editMessageViewKey.currentState?.onClose();

0 commit comments

Comments
 (0)