# Message Actions

Customizing Message Actions

### Introduction

Message actions appear in an overlay when you long-press a message.

![](@chat-sdk/flutter/v10-latest/_assets/message_widget_actions.png)

By default, the SDK renders the following message actions:

- Reply
- Thread reply
- Edit message (own messages only)
- Copy message
- Delete message (own messages only)
- Flag message
- Pin message
- Mark unread

<admonition type="note">

Edit and delete are only available on messages sent by the current user.
Pinning a message requires the appropriate channel role permissions.
Mark unread is only available on messages from other users when read events are enabled.

</admonition>

### StreamContextMenuAction

Message actions are now rendered as `StreamContextMenuAction<T>` widgets (from `stream_core_flutter`), which are self-rendering and support two dispatch mechanisms:

- **`value`** — when tapped inside a popup route, pops the route with this value, then calls `onTap` if provided
- **`onTap`** — a `VoidCallback?` for inline usage or additional side effects after route dismissal

### Adding a Custom Action via `actionsBuilder`

Use `actionsBuilder` on `StreamMessageWidgetProps` to append or modify actions:

```dart
StreamMessageListView(
  messageBuilder: (context, message, defaultProps) {
    return StreamMessageWidget.fromProps(
      props: defaultProps.copyWith(
        actionsBuilder: (context, defaultActions) => [
          ...defaultActions,
          StreamContextMenuAction(
            leading: const Icon(Icons.star),
            label: const Text('Favourite'),
            onTap: () => _favourite(message),
          ),
        ],
      ),
    );
  },
)
```

### Removing a Default Action

Filter the `defaultActions` list to remove specific actions:

```dart
actionsBuilder: (context, defaultActions) => [
  ...defaultActions.where((a) => a.props.value is! DeleteMessage),
]
```

### App-wide Custom Actions via Component Factory

To apply actions changes across the entire app, use `StreamComponentFactory`:

```dart
StreamChat(
  client: client,
  componentBuilders: StreamComponentBuilders(
    extensions: streamChatComponentBuilders(
      messageWidget: (context, props) {
        return DefaultStreamMessage(
          props: props.copyWith(
            actionsBuilder: (context, defaultActions) {
              return StreamContextMenuAction.partitioned(
                items: [
                  ...defaultActions,
                  StreamContextMenuAction(
                    leading: Icon(context.streamIcons.informationCircle),
                    label: const Text('Info'),
                    onTap: () => showInfo(props.message),
                  ),
                ],
              );
            },
          ),
        );
      },
    ),
  ),
  child: ...,
)
```

### Destructive Actions

Use the `.destructive` constructor for actions that should be styled as destructive:

```dart
StreamContextMenuAction<MessageAction>.destructive(
  value: DeleteMessage(message: message),
  leading: Icon(context.streamIcons.trashBin),
  label: const Text('Delete'),
)
```

### Grouping Actions with Separators

Helper methods automatically insert `StreamContextMenuSeparator` widgets between items:

```dart
// Insert a separator between every item
StreamContextMenuAction.separated(items: actions)

// Group into normal / destructive sections with separator between them
StreamContextMenuAction.partitioned(items: actions)

// Custom sections
StreamContextMenuAction.sectioned(sections: [normalActions, destructiveActions])
```

### showStreamDialog

Use `showStreamDialog<T>` instead of `showDialog` when presenting Stream modals. It re-wraps `StreamChatTheme` across the route boundary and applies a consistent transition:

```dart
final action = await showStreamDialog<MessageAction>(
  context: context,
  builder: (_) => StreamMessageActionsModal(
    message: message,
    messageWidget: messageWidget,
    messageActions: [
      StreamContextMenuAction<MessageAction>(
        value: CopyMessage(message: message),
        leading: Icon(context.streamIcons.copy),
        label: Text(context.translations.copyMessageLabel),
        onTap: () => _copyMessage(message),
      ),
    ],
  ),
);

if (action is CopyMessage) _copyMessage(action.message);
```

### StreamMessageActionsModal

`StreamMessageActionsModal.messageActions` now accepts `List<Widget>` instead of `List<StreamMessageAction>`. The `onActionTap` callback has been removed — handle dispatch via `onTap` on each action or by awaiting the dialog return value.

### StreamContextMenu and StreamContextMenuSeparator

For custom inline action menus, use `StreamContextMenu` as a themed container:

```dart
StreamContextMenu(
  children: [
    StreamContextMenuAction<MessageAction>(
      value: reply,
      label: const Text('Reply'),
    ),
    const StreamContextMenuSeparator(),
    StreamContextMenuAction<MessageAction>.destructive(
      value: delete,
      label: const Text('Delete'),
    ),
  ],
)
```


---

This page was last updated at 2026-04-23T18:43:04.005Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/flutter/v10/stream_chat_flutter/custom_widgets/customize_message_actions/](https://getstream.io/chat/docs/sdk/flutter/v10/stream_chat_flutter/custom_widgets/customize_message_actions/).