Skip to main content
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

Default context menu

The default context menu includes:
  • Cut - Cut selected content
  • Copy - Copy selected content
  • Paste - Paste from clipboard

ContextMenuItem structure

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

ContextMenuItem properties

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.

Creating custom menu items

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;
  },
);

Grouping menu items

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

Conditional menu items

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;
  },
);

Applying custom context menu

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
    );
  }
}

Custom context menu builder

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'),
            ),
          ],
        ),
      ),
    ),
  );
}

Built-in menu items

AppFlowy Editor provides built-in context menu items:
import 'package:appflowy_editor/appflowy_editor.dart';

// Standard clipboard items
final standardItems = [
  [cutMenuItem, copyMenuItem, pasteMenuItem],
];

Platform-specific behavior

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.

Disabling context menu

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

Build docs developers (and LLMs) love