The context menu appears when you right-click (or long-press on mobile) in the editor. It provides quick access to common editing actions.
Overview
The context menu provides:
- Copy, cut, and paste operations
- Text formatting options
- Block operations (delete, duplicate)
- Custom menu items
The default context menu includes:
- Cut - Cut selected content
- Copy - Copy selected content
- Paste - Paste from clipboard
Context menu items are defined using the ContextMenuItem class:
import 'package:appflowy_editor/appflowy_editor.dart';
final copyItem = ContextMenuItem(
getName: () => 'Copy',
onPressed: (editorState) {
final selection = editorState.selection;
if (selection == null) return;
// Copy selection to clipboard
editorState.copy();
},
isApplicable: (editorState) {
// Only show if there's a selection
final selection = editorState.selection;
return selection != null && !selection.isCollapsed;
},
);
Source: lib/src/service/context_menu/context_menu.dart:11-23
getName
String Function()
required
Function that returns the display name of the menu item
onPressed
void Function(EditorState)
required
Callback executed when the menu item is clicked
isApplicable
bool Function(EditorState)?
Optional function to determine if the item should be shown. Returns true to show the item.
Create custom context menu items for your needs:
import 'package:appflowy_editor/appflowy_editor.dart';
final duplicateItem = ContextMenuItem(
getName: () => 'Duplicate',
onPressed: (editorState) {
final selection = editorState.selection;
if (selection == null) return;
final node = editorState.getNodeAtPath(selection.start.path);
if (node == null) return;
// Duplicate the node
final transaction = editorState.transaction;
transaction.insertNode(
selection.start.path.next,
node.copyWith(),
);
editorState.apply(transaction);
},
isApplicable: (editorState) {
return editorState.selection != null;
},
);
final deleteBlockItem = ContextMenuItem(
getName: () => 'Delete Block',
onPressed: (editorState) {
final selection = editorState.selection;
if (selection == null) return;
final transaction = editorState.transaction;
transaction.deleteNode(editorState.getNodeAtPath(selection.start.path)!);
editorState.apply(transaction);
},
isApplicable: (editorState) {
return editorState.selection != null;
},
);
Context menu items can be organized into groups (separated by dividers):
final contextMenuItems = [
[
// Clipboard group
cutItem,
copyItem,
pasteItem,
],
[
// Formatting group
boldItem,
italicItem,
underlineItem,
],
[
// Block operations group
duplicateItem,
deleteBlockItem,
],
];
Source: lib/src/service/context_menu/context_menu.dart:25-82
Use isApplicable to show menu items conditionally:
final formatHeadingItem = ContextMenuItem(
getName: () => 'Convert to Heading',
onPressed: (editorState) {
final selection = editorState.selection;
if (selection == null) return;
// Convert to heading
final node = editorState.getNodeAtPath(selection.start.path);
if (node == null) return;
final transaction = editorState.transaction;
transaction.updateNode(node, {
'type': HeadingBlockKeys.type,
'data': {
'level': 1,
},
});
editorState.apply(transaction);
},
isApplicable: (editorState) {
final selection = editorState.selection;
if (selection == null) return false;
final node = editorState.getNodeAtPath(selection.start.path);
// Only show for paragraph blocks
return node?.type == ParagraphBlockKeys.type;
},
);
Provide custom context menu items to the editor:
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
class EditorWithCustomContextMenu extends StatelessWidget {
const EditorWithCustomContextMenu({super.key});
@override
Widget build(BuildContext context) {
return AppFlowyEditor(
editorState: EditorState.blank(),
// Custom context menu will be applied
);
}
}
For complete control over the context menu UI, provide a custom builder:
import 'package:appflowy_editor/appflowy_editor.dart';
Widget customContextMenuBuilder(
BuildContext context,
Offset position,
EditorState editorState,
VoidCallback onPressed,
) {
return Positioned(
top: position.dy,
left: position.dx,
child: Material(
elevation: 8,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.all(8),
child: Column(
children: [
// Your custom menu UI
TextButton(
onPressed: () {
// Action
onPressed();
},
child: const Text('Custom Action'),
),
],
),
),
),
);
}
AppFlowy Editor provides built-in context menu items:
import 'package:appflowy_editor/appflowy_editor.dart';
// Standard clipboard items
final standardItems = [
[cutMenuItem, copyMenuItem, pasteMenuItem],
];
Context menus adapt to the platform:
- Desktop: Right-click to show context menu
- Mobile: Long-press to show context menu
- Web: Right-click (browser context menu may appear)
On mobile platforms, the context menu appears after a long-press gesture. Make sure menu items have touch-friendly sizes.
To disable the context menu:
AppFlowyEditor(
editorState: editorState,
// Disable by not providing context menu items
)
When customizing the context menu, ensure essential operations like copy and paste remain accessible to users.
Toolbar Customization
Customize the floating toolbar
Selection Menu
Customize the slash command menu