Understanding the document hierarchy and node-based architecture
AppFlowy Editor uses a tree-based document structure where each element is represented as a Node. The Document class serves as the container for the root node and provides methods for querying and manipulating the document tree.
class Document { // The root node of the document final Node root; // First node of the document Node? get first => root.children.firstOrNull; // Last node of the document Node? get last { Node? current = root.children.lastOrNull; while (current != null && current.children.isNotEmpty) { current = current.children.last; } return current; } // Check if document is empty bool get isEmpty;}
class Node { // The type determines which block component renders this node final String type; // Unique identifier (auto-generated if not provided) String id; // Parent node reference Node? parent; // Child nodes List<Node> get children; // Node attributes/data Attributes get attributes; // Path from root to this node Path get path;}
// Get node at pathfinal node = document.nodeAtPath([0, 1]);// Get first nodefinal first = document.first;// Get last nodefinal last = document.last;// Check if emptyif (document.isEmpty) { // Handle empty document}
// These methods exist but should be called via transactionsdocument.insert(path, nodes); // ❌ Direct use not recommendeddocument.delete(path, length); // ❌ Direct use not recommendeddocument.update(path, attributes); // ❌ Direct use not recommended
Always use transactions to modify the document. Direct mutation bypasses undo/redo and collaborative editing support.
// Get all childrenfinal children = node.children;// Get child at indexfinal child = node.childAtIndexOrNull(0);// Get child at pathfinal descendant = node.childAtPath([0, 1]);
// Create a deep copy of a nodefinal copy = node.copyWith();// Copy with modificationsfinal modified = node.copyWith( type: 'heading', attributes: {'level': 2},);// Deep copy (same as copyWith with no params)final deepCopy = node.deepCopy();