Skip to main content

Overview

The Quill Delta plugin enables importing content from editors that use the Quill Delta format. This provides compatibility with Quill-based editors and applications.
This plugin currently supports importing only (Quill Delta → AppFlowy Document). Export from AppFlowy to Quill Delta is not yet supported.

Installation

The Quill Delta encoder is included with AppFlowy Editor:
import 'package:appflowy_editor/appflowy_editor.dart';

What is Quill Delta?

Quill Delta is a JSON-based format used by the Quill editor to represent rich text documents. It consists of an array of operations that describe document content and formatting. Example Quill Delta:
[
  {"insert": "Hello AppFlowy!"},
  {"attributes": {"header": 1}, "insert": "\n"}
]

Importing Quill Delta

Basic Usage

Convert Quill Delta JSON to an AppFlowy document:
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);

Complete Example

From the documentation:
// From documentation/importing.md:38-44
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);

Supported Quill Attributes

The Quill Delta encoder supports these attributes:

Block-Level Attributes

  • Headers: {"header": 1-6} → Heading levels 1-6
  • Lists:
    • {"list": "bullet"} → Bulleted list
    • {"list": "ordered"} → Numbered list
    • {"list": "checked"} → Checked todo list
    • {"list": "unchecked"} → Unchecked todo list
  • Block quote: {"blockquote": true} → Quote block
  • Indentation: {"indent": 1-9} → Indented content

Text-Level Attributes

  • Bold: {"bold": true}
  • Italic: {"italic": true}
  • Underline: {"underline": true}
  • Strikethrough: {"strike": true}
  • Links: {"link": "url"}
  • Text color: {"color": "#FF0000"} or {"color": "rgba(255,0,0,1)"}
  • Background color: {"background": "#FFFF00"} or {"background": "rgba(255,255,0,1)"}

Implementation Details

Quill Delta Encoder Class

The encoder is implemented in /lib/src/plugins/quill_delta/quill_delta_encoder.dart:18-236:
final QuillDeltaEncoder quillDeltaEncoder = QuillDeltaEncoder();

class QuillDeltaEncoder extends Converter<Delta, Document> {
  @override
  Document convert(Delta input) {
    final iterator = input.iterator;
    final document = Document.blank(withInitialText: false);
    Node node = paragraphNode();
    // ... conversion logic
    return document;
  }
}

Nested Lists

The encoder handles nested lists using indentation:
// From quill_delta_encoder.dart:40-49
if (_isIndentBulletedList(attributes)) {
  final level = _indentLevel(attributes);
  final path = [
    ...nestedLists[level - 1]!.last.path,
    nestedLists[level]!.length - 1,
  ];
  document.insert(path, [node]);
}

Color Conversion

Quill color formats are converted to AppFlowy format:
// Hex colors
final color = attributes?['color'] as String?;
if (color != null && color.startsWith('#')) {
  attrs[AppFlowyRichTextKeys.textColor] = '0xFF${color.substring(1)}';
}

// RGBA colors
if (color != null && color.startsWith('rgba')) {
  // Parse and convert to Flutter Color
}

Handling Special Cases

Multiline Text Inserts

The encoder handles text containing newlines:
// From quill_delta_encoder.dart:52-57
final texts = op.text.split('\n');
if (texts.length > 1) {
  node.delta?.insert(texts[0]);
  document.insert([index++], [node]);
  node = paragraphNode(delta: Delta()..insert(texts[1]));
}

Indentation Without Lists

Indentation on regular paragraphs is converted to spaces:
// From quill_delta_encoder.dart:106-120
void _applyIndentIfNeeded(Node node, Map<String, dynamic> attributes) {
  final indent = attributes[_indent] as int?;
  final list = attributes[_list] as String?;
  if (indent != null && list == null && node.delta != null) {
    node.updateAttributes({
      'delta': node.delta
        ?.compose(
          Delta()
            ..retain(0)
            ..insert('  ' * indent),
        )
        .toJson(),
    });
  }
}

Limitations

Some Quill features are not yet supported during conversion:
  • Font size and font family
  • Text alignment (left, center, right, justify)
  • Code blocks (inline code is supported)
  • Images and embeds
  • Custom attributes

Error Handling

The encoder only supports text insert operations:
if (op is TextInsert) {
  // Handle text insertion
} else {
  throw UnsupportedError('only support text insert operation');
}

Migration Guide

If you’re migrating from a Quill-based editor:
  1. Export your Quill content as Delta JSON
  2. Parse the JSON to a Delta object
  3. Use quillDeltaEncoder.convert() to create an AppFlowy document
  4. Initialize your EditorState with the converted document
// Step 1: Get Quill Delta JSON from your Quill editor
final quillJson = quillEditor.getContents();

// Step 2: Convert to Delta object
final delta = Delta.fromJson(quillJson);

// Step 3: Convert to AppFlowy document
final document = quillDeltaEncoder.convert(delta);

// Step 4: Create EditorState
final editorState = EditorState(document: document);

Example: Complex Document

final json = r'''[
  {"insert": "Project Tasks"},
  {"attributes": {"header": 1}, "insert": "\n"},
  {"insert": "Complete documentation"},
  {"attributes": {"list": "checked"}, "insert": "\n"},
  {"insert": "Review pull requests"},
  {"attributes": {"list": "unchecked"}, "insert": "\n"},
  {"insert": "Important", "attributes": {"bold": true, "color": "#FF0000"}},
  {"insert": ": Deploy by Friday\n"}
]''';

final delta = Delta.fromJson(jsonDecode(json));
final document = quillDeltaEncoder.convert(delta);
final editorState = EditorState(document: document);

See Also

Build docs developers (and LLMs) love