fix: 🐛 ensure images are properly added to preview when sending messages with text#430
fix: 🐛 ensure images are properly added to preview when sending messages with text#430japanshah-simform wants to merge 1 commit into
Conversation
7b71d96 to
d6674e8
Compare
d6674e8 to
761c6c3
Compare
There was a problem hiding this comment.
Pull request overview
Fixes image-preview behavior when shouldSendImageWithText is enabled by providing a public API to programmatically add selected images into the SendMessageWidget preview (useful for custom trailing actions).
Changes:
- Refactors SendMessageWidget image-selection logic into
handleImageSelection. - Adds
ChatView.handleImageSelection(context, imagePath)for external/custom action buttons to add images to the preview. - Updates the example and docs to demonstrate the new workflow and enables
shouldSendImageWithTextin the sample.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/src/widgets/send_message_widget.dart | Extracts image-selection/preview logic into handleImageSelection and routes onImageSelected through it. |
| lib/src/widgets/chat_view.dart | Adds a static helper to forward image selection into the SendMessageWidget state. |
| example/lib/main.dart | Demonstrates adding an image to the preview via ChatView.handleImageSelection. |
| doc/documentation.md | Updates guidance for sending images with text and using the new API (currently contains invalid snippet). |
| CHANGELOG.md | Adds an unreleased changelog entry for the new fix/API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ChatView.handleImageSelection to use shouldSendImageWithText | ||
| parameter with custom trailing actions. |
There was a problem hiding this comment.
Changelog entry wording is a bit unclear/grammatically awkward (“Added … to use … parameter …”). Consider rephrasing to something like: “Added ChatView.handleImageSelection to support shouldSendImageWithText when using custom trailingActions.”
| ChatView.handleImageSelection to use shouldSendImageWithText | |
| parameter with custom trailing actions. | |
| `ChatView.handleImageSelection` to support `shouldSendImageWithText` | |
| when using custom `trailingActions`. |
| onImageSelected: (images, messageId) { | ||
| if (widget.sendMessageConfig | ||
| .shouldSendImageWithText) { | ||
| if (images.isNotEmpty) { | ||
| _selectedImageViewWidgetKey.currentState | ||
| ?.selectedImages.value = [ | ||
| ...?_selectedImageViewWidgetKey | ||
| .currentState?.selectedImages.value, | ||
| images | ||
| ]; | ||
|
|
||
| FocusScope.of(context) | ||
| .requestFocus(_focusNode); | ||
| } | ||
| } else { | ||
| _onImageSelected(images, ''); | ||
| } | ||
| handleImageSelection(images); | ||
| }, |
There was a problem hiding this comment.
The onImageSelected callback parameter messageId is unused, which triggers the unused_parameter analyzer warning unless it’s named _. Consider changing the callback signature to (imagePath, _) (and renaming images to imagePath for clarity) before calling handleImageSelection(imagePath).
| void handleImageSelection(String images) { | ||
| if (widget.sendMessageConfig.shouldSendImageWithText) { | ||
| if (images.isNotEmpty) { | ||
| _selectedImageViewWidgetKey.currentState?.selectedImages.value = [ | ||
| ...?_selectedImageViewWidgetKey.currentState?.selectedImages.value, | ||
| images | ||
| ]; | ||
| FocusScope.of(context).requestFocus(_focusNode); | ||
| } | ||
| } else { | ||
| _onImageSelected(images, ''); |
There was a problem hiding this comment.
handleImageSelection takes a single image path, but the parameter is named images, which is misleading given the surrounding code uses a list of selected images. Renaming the parameter to imagePath (and updating internal references) would reduce confusion.
| void handleImageSelection(String images) { | |
| if (widget.sendMessageConfig.shouldSendImageWithText) { | |
| if (images.isNotEmpty) { | |
| _selectedImageViewWidgetKey.currentState?.selectedImages.value = [ | |
| ...?_selectedImageViewWidgetKey.currentState?.selectedImages.value, | |
| images | |
| ]; | |
| FocusScope.of(context).requestFocus(_focusNode); | |
| } | |
| } else { | |
| _onImageSelected(images, ''); | |
| void handleImageSelection(String imagePath) { | |
| if (widget.sendMessageConfig.shouldSendImageWithText) { | |
| if (imagePath.isNotEmpty) { | |
| _selectedImageViewWidgetKey.currentState?.selectedImages.value = [ | |
| ...?_selectedImageViewWidgetKey.currentState?.selectedImages.value, | |
| imagePath | |
| ]; | |
| FocusScope.of(context).requestFocus(_focusNode); | |
| } | |
| } else { | |
| _onImageSelected(imagePath, ''); |
| trailingActions: (context, controller) => [ | ||
| GalleryActionButton( | ||
| icon: Icon( | ||
| Icons.photo_rounded, | ||
| size: 30, | ||
| color: _theme.iconColor, | ||
| ), | ||
| onPressed: (path, replyMessage) { | ||
| ChatView.handleImageSelection(context, path!); | ||
| }, |
There was a problem hiding this comment.
The documentation example is using trailingActions directly on SendMessageConfiguration, but that property is on TextFieldConfiguration (sendMessageConfig.textFieldConfig.trailingActions). As written, the snippet won’t compile and may mislead users.
| trailingActions: (context, controller) => [ | |
| GalleryActionButton( | |
| icon: Icon( | |
| Icons.photo_rounded, | |
| size: 30, | |
| color: _theme.iconColor, | |
| ), | |
| onPressed: (path, replyMessage) { | |
| ChatView.handleImageSelection(context, path!); | |
| }, | |
| textFieldConfig: TextFieldConfiguration( | |
| trailingActions: (context, controller) => [ | |
| GalleryActionButton( | |
| icon: Icon( | |
| Icons.photo_rounded, | |
| size: 30, | |
| color: _theme.iconColor, | |
| ), | |
| onPressed: (path, replyMessage) { | |
| ChatView.handleImageSelection(context, path!); | |
| }, | |
| ), | |
| ], |
| icon: Icon( | ||
| Icons.photo_rounded, | ||
| size: 30, | ||
| color: _theme.iconColor, | ||
| ), | ||
| onPressed: (path, replyMessage) { | ||
| ChatView.handleImageSelection(context, path!); | ||
| }, | ||
| ), |
There was a problem hiding this comment.
The GalleryActionButton snippet in this code block has mismatched indentation/parentheses and is missing the closing ] / ) needed to end trailingActions and SendMessageConfiguration, so the example won’t compile as-is. Please fix the bracket/parenthesis structure so the snippet is valid Dart.
| icon: Icon( | |
| Icons.photo_rounded, | |
| size: 30, | |
| color: _theme.iconColor, | |
| ), | |
| onPressed: (path, replyMessage) { | |
| ChatView.handleImageSelection(context, path!); | |
| }, | |
| ), | |
| icon: Icon( | |
| Icons.photo_rounded, | |
| size: 30, | |
| color: _theme.iconColor, | |
| ), | |
| onPressed: (path, replyMessage) { | |
| ChatView.handleImageSelection(context, path!); | |
| }, | |
| ), | |
| ], |
| color: _theme.iconColor, | ||
| ), | ||
| onPressed: (path, replyMessage) { | ||
| ChatView.handleImageSelection(context, path!); |
There was a problem hiding this comment.
The example calls ChatView.handleImageSelection(context, path!) without guarding for path == null (e.g., user cancels picker). Since path is nullable in the action callbacks, the docs should show a null/empty check (as in the example app) to avoid runtime exceptions.
| ChatView.handleImageSelection(context, path!); | |
| if (path != null && path.isNotEmpty) { | |
| ChatView.handleImageSelection(context, path); | |
| } |
53c6032 to
b6566fc
Compare
| onImageSelected: (imagePath, _) { | ||
| _onImageSelected(imagePath, ''); |
| /// | ||
| /// Displays the selected image full-screen with: | ||
| /// - A close button at the top-left | ||
| /// - An chat name in the top bar |
| onSend: (imagePath, caption, reply) { | ||
| _chatController.addMessage( | ||
| Message( | ||
| id: '${DateTime.now().microsecondsSinceEpoch}_img', |
| if (caption.isNotEmpty) { | ||
| _chatController.addMessage( | ||
| Message( | ||
| id: '${DateTime.now().microsecondsSinceEpoch}_cap', |
| `shouldSendImageWithText` parameter. Images are now sent through an image preview screen in example that | ||
| supports optional text captions. Use `GalleryActionButton` with a custom preview handler to | ||
| achieve similar functionality. |
| You can customize the view for sending images by using the `GalleryActionButton` in `trailingActions`. Here's a practical example that opens an image preview screen before sending: | ||
|
|
b6566fc to
f5c40d8
Compare
| children: [ | ||
| Positioned.fill( | ||
| child: InteractiveViewer( | ||
| minScale: 0.8, |
There was a problem hiding this comment.
Is there any specific requirement for keeping minScale at 0.8 here? Allowing the image to shrink below its initial size may not be necessary for the preview screen.
| maxScale: 4.0, | ||
| child: Center( | ||
| child: Image.file( | ||
| File(widget.imagePath), |
There was a problem hiding this comment.
Image.file(File(...)) crashes on Flutter Web because dart:io is unsupported. Please take care of web compatibility here as well.
| } | ||
|
|
||
| void _onImageSelected(String imagePath, String error) { | ||
| void _onImageSelected(String imagePath, String messageId) { |
There was a problem hiding this comment.
messageId is added as a parameter but is never used inside the method body. Please remove the unnecessary parameter if it is not needed.
| chatName: widget.chat.name, | ||
| onSend: (imagePath, caption, reply) { | ||
| var timeStamp = | ||
| DateTime.now().microsecondsSinceEpoch; |
There was a problem hiding this comment.
timeStamp is never reassigned after initialisation, so final would be more appropriate here than var.
|
|
||
| ## Send Image With Message | ||
| You can send images along with your messages by enabling the `shouldSendImageWithText` flag in `sendMessageConfig` all the other things will be handled by the package itself. Here's how to do it: | ||
| ## Send Images |
There was a problem hiding this comment.
The section title Send Images feels a bit too generic now. Since this flow also includes a preview screen and optional caption handling, a more descriptive title may improve clarity in the documentation.
| chatName: widget.chat.name, | ||
| onSend: (imagePath, caption, reply) { | ||
| // Create a timestamp for unique message IDs | ||
| var timeStamp = DateTime.now().microsecondsSinceEpoch; |
f5c40d8 to
307d0a7
Compare
Description
Before:
Screen.Recording.2026-01-30.at.2.44.17.PM.mov
After:
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2026-05-15.at.12.mp4
Checklist
fix:,feat:,docs:etc).docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues