AppFlowy Editor Documentation

repository·main·Indexed 20 days ago

https://github.com/appflowy-io/appflowy-editor

A highly customizable rich-text editor built for Flutter featuring extensible block components, custom themes, and configurable shortcut events. The library supports data import from AppFlowy Document JSON, Markdown, and Quill Delta, and provides tools for customizing EditorStyle and BlockComponentConfiguration.

Tokens
15.3K
Snippets
41
Records
59
Agent score
69%

What's inside AppFlowy Editor

  1. Customize the AppFlowy Editor

    main

    The AppFlowy Editor is designed to be highly extensible. You can customize several core aspects of the user experience:

    • Themes: Modify the visual appearance of the editor.
    • Block Components: Create and render new types of content blocks (e.g., custom form inputs, dividers, or specialized lists).
    • Shortcut Events: Define custom keyboard shortcuts to trigger specific actions, such as text formatting (Bold, Italic, Underline, Strikethrough).
  2. Import data from Markdown

    main

    To initialize the editor with Markdown content, use the markdownToDocument(markdown) function to convert a Markdown string into a Document object. This object is then used to create the EditorState.

    const markdown = r'''# Hello AppFlowy!''';
    final editorState = EditorState(
      document: markdownToDocument(markdown),
    );
  3. Customize the editor theme with EditorStyle and BlockComponentConfiguration

    main

    The editor's visual appearance is controlled by two main components:

    1. EditorStyle: Controls global editor properties such as padding, cursor color, selection color, and TextStyleConfiguration (styles for normal text, bold, href, and code). It also supports a textSpanDecorator for custom logic when rendering text spans (e.g., adding tap recognizers to links).

    2. BlockComponentConfiguration: Used within block builders to define block-specific styles like padding and textStyle based on the block type. This is passed to specific block builders (like HeadingBlockComponentBuilder or TodoListBlockComponentBuilder).

    To apply a custom theme, pass your EditorStyle to the editorStyle parameter and your custom map of builders to the blockComponentBuilders parameter in the AppFlowyEditor widget.

    // Define global styles
    EditorStyle customizeEditorStyle() {
      return EditorStyle(
        padding: const EdgeInsets.symmetric(horizontal: 20),
        cursorColor: Colors.green,
        selectionColor: Colors.green,
        textStyleConfiguration: TextStyleConfiguration(
          text: const TextStyle(fontSize: 18.0, color: Colors.white54),
          bold: const TextStyle(fontWeight: FontWeight.w900),
          href: const TextStyle(color: Colors.amber),
          code: const TextStyle(fontSize: 14.0, color: Colors.blue),
        ),
      );
    }
    
    // Define block-specific styles and builders
    Map<String, BlockComponentBuilder> customBuilder() {
      final configuration = BlockComponentConfiguration(
        padding: (node) => const EdgeInsets.symmetric(vertical: 10),
        textStyle: (node) => const TextStyle(color: Colors.yellow),
      );
    
      return {
        ...standardBlockComponentBuilderMap,
        HeadingBlockKeys.type: HeadingBlockComponentBuilder(configuration: configuration),
        TodoListBlockKeys.type: TodoListBlockComponentBuilder(
          configuration: configuration,
          iconBuilder: (context, node) => Icon(Icons.check_box),
        ),
      };
    }
    
    // Apply to the editor
    AppFlowyEditor(
      editorState: EditorState.blank(),
      editorStyle: customizeEditorStyle(),
      blockComponentBuilders: customBuilder(),
    )
  4. Add a new language to the editor

    main

    To support a new language in the AppFlowy Editor, follow these steps:

    1. Locate the translation directory: lib/l10n/.
    2. Install the Flutter intl plugin for Visual Studio Code.
    3. Use intl_en.arb as a template: copy it and rename the copy to intl_<new_locale>.arb (e.g., intl_fr.arb).
    4. Modify the new .arb file with your translations.
    5. Save the file to trigger automatic translation generation.
  5. Modify the editor locale for testing

    main

    To test new translations, you can force the editor to use a specific locale in the example application.

    Static Method

    In example/lib/main.dart, replace the default supportedLocales assignment with a specific Locale list:

    // Change this:
    supportedLocales: AppFlowyEditorLocalizations.delegate.supportedLocales,
    // To a specific locale:
    supportedLocales: const [Locale('fr', 'FR')],

    Interactive Method

    You can implement a toggle in example/lib/home_page.dart using AppFlowyEditorLocalizations.load() to switch locales at runtime. Note that you must rebuild the application to see the changes in translated strings.

    Note: You must rebuild the app to see the changes of the translated strings.

    // example/lib/home_page.dart
    void toggleLocale() {
      final locale = Intl.getCurrentLocale();
      if (locale.startsWith('en')) {
        // Change to the locale you want to test
        AppFlowyEditorLocalizations.load(const Locale('pt', 'BR')); 
      } else {
        AppFlowyEditorLocalizations.load(const Locale('en', 'US'));
      }
    }
  6. Testing Basic Editor Functions

    main

    When writing tests for the Appflowy Editor, you can manipulate the document state and simulate user interactions using the editor instance provided by your test infrastructure.

    Setup

    You must call editor.startTesting() before performing any testing operations.

    Document Manipulation

    • Insert Nodes: Use editor.insertEmptyTextNode() for empty nodes or editor.insertTextNode(text, {attributes}) to insert text with specific styles.
    • Styling with Attributes: Use BuiltInAttributeKey to apply styles like heading or bulletedList. For complex styling (e.g., bolding specific parts of an insertion), use the delta parameter with a Delta object containing TextInsert operations.
    • Text Insertion: Use editor.insertText(node, text, offset) to insert text at a specific position within a node.

    Selection and Navigation

    • Accessing Nodes: Use editor.nodeAtPath(path) to retrieve a node at a specific location (e.g., [0] for the first node).
    • Updating Selection: Use editor.updateSelection(Selection.single(path: ..., startOffset: ...)) to programmatically move the cursor or select text.
    • Querying Selection: Access editor.documentSelection to get the current selection state.

    Simulating Input

    • Keyboard Shortcuts: Use editor.pressLogicKey(key, {isMetaPressed, isShiftPressed}) to simulate key presses like Meta + A (Select All).
    // Setup
    await editor.startTesting();
    
    // Insert text with heading style
    editor.insertTextNode('Hello', attributes: {
        BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
        BuiltInAttributeKey.heading: BuiltInAttributeKey.h1,
    });
    
    // Insert text with complex styling via Delta
    editor.insertTextNode('', 
        attributes: {BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList}, 
        delta: Delta([
            TextInsert('Bold Text', {BuiltInAttributeKey.bold: true}),
        ]),
    );
    
    // Simulate Meta + A
    await editor.pressLogicKey(LogicalKeyboardKey.keyA, isMetaPressed: true);
  7. Migrate from AppFlowyEditor 1.1 to 1.2

    main

    When upgrading from version 1.1 to 1.2, several breaking changes were introduced regarding component naming and constructor parameters:

    1. Editor Constructors: AppFlowyEditor.custom and AppFlowyEditor.standard have been removed. Use the main AppFlowyEditor constructor instead. If you do not provide custom values for blockComponentBuilders, characterShortcutEvents, or commandShortcutEvents, the editor will now automatically provide default values.
    2. Mixin Renaming: DefaultSelectable has been renamed to DefaultSelectableMixin.
    3. Rich Text Renaming: FlowyRichText has been renamed to AppFlowyRichText.
    4. Decorator Updates: The TextSpanDecoratorForAttribute now includes a new required context parameter of type BuildContext.
  8. Customize the iOS launch screen assets

    main

    To change the image displayed during the app's launch on iOS, you can either replace the image files directly in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory or use Xcode to manage the assets.

    If using Xcode:

    1. Open the iOS project workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog.
    open ios/Runner.xcworkspace
  9. Import data from AppFlowy Document JSON

    main

    You can initialize the EditorState using a JSON structure that follows the AppFlowy Document format. Use Document.fromJson(json) to convert the decoded Map into a Document object, which is then passed to the EditorState constructor.

    const document = r'''{
      "document": {
        "type": "page",
        "children": [
          {
            "type": "heading",
            "data": {
                "delta": [{ "insert": "Hello AppFlowy!" }],
                "level": 1
            }
          }
        ]
      }
    }''';
    final json = Map<String, Object>.from(jsonDecode(document));
    final editorState = EditorState(
      document: Document.fromJson(json),
    );
  10. Import data from Quill Delta

    main

    If you have Quill Delta JSON, you can convert it to an AppFlowy Document using a quillDeltaEncoder. First, decode the JSON into a Delta object using Delta.fromJson(), then call quillDeltaEncoder.convert(delta) to produce the Document required for EditorState.

    const json = r'''[{"insert":"Hello AppFlowy!"},{"attributes":{"header":1},"insert":"\n"}]''';
    final delta = Delta.fromJson(jsonDecode(json));
    final document = quillDeltaEncoder.convert(delta);
    final editorState = EditorState(document: document);