Skip to main content

Overview

Paragraph blocks are the most fundamental text block type in AppFlowy Editor. They support rich text formatting, text direction, alignment, and background colors.

Block Keys

Paragraph blocks use the following keys:
class ParagraphBlockKeys {
  static const String type = 'paragraph';
  static const String delta = blockComponentDelta;
  static const String backgroundColor = blockComponentBackgroundColor;
  static const String textDirection = blockComponentTextDirection;
}

Creating Paragraph Nodes

You can create paragraph nodes using the paragraphNode() helper function:
// Create a simple paragraph
final paragraph = paragraphNode(
  text: 'Hello, world!',
);

// Create with Delta (rich text)
final richParagraph = paragraphNode(
  delta: Delta()
    ..insert('Hello, ')
    ..insert('world', attributes: {'bold': true})
    ..insert('!'),
);

// With text direction
final rtlParagraph = paragraphNode(
  text: 'مرحبا بالعالم',
  textDirection: 'rtl',
);

// With custom attributes
final styledParagraph = paragraphNode(
  text: 'Styled text',
  attributes: {
    'backgroundColor': '0xFFFFFF00',
  },
);
Source: /lib/src/editor/block_component/paragraph_block_component/paragraph_block_component.dart:16

Paragraph Component Builder

Create a custom paragraph component with configuration:
final paragraphBuilder = ParagraphBlockComponentBuilder(
  configuration: BlockComponentConfiguration(
    padding: (node) => const EdgeInsets.symmetric(
      vertical: 8.0,
      horizontal: 16.0,
    ),
    placeholderText: (node) => 'Type / for commands',
    textStyle: (node, {textSpan}) => const TextStyle(
      fontSize: 16.0,
      height: 1.5,
    ),
  ),
  showPlaceholder: (editorState, node) {
    // Custom logic to show/hide placeholder
    final selection = editorState.selection;
    return selection != null && 
           selection.isSingle && 
           selection.start.path.equals(node.path);
  },
);
Source: /lib/src/editor/block_component/paragraph_block_component/paragraph_block_component.dart:38

Widget Structure

The paragraph block widget is structured as follows:
class ParagraphBlockComponentWidget extends BlockComponentStatefulWidget {
  const ParagraphBlockComponentWidget({
    super.key,
    required super.node,
    super.showActions,
    super.actionBuilder,
    super.actionTrailingBuilder,
    super.configuration = const BlockComponentConfiguration(),
    this.showPlaceholder,
  });

  final ShowPlaceholder? showPlaceholder;
}

Mixins Used

The paragraph component state uses several mixins:
class _ParagraphBlockComponentWidgetState
    extends State<ParagraphBlockComponentWidget>
    with
        SelectableMixin,
        DefaultSelectableMixin,
        BlockComponentConfigurable,
        BlockComponentBackgroundColorMixin,
        NestedBlockComponentStatefulWidgetMixin,
        BlockComponentTextDirectionMixin,
        BlockComponentAlignMixin {
  // ...
}
Functionality Provided:
  • SelectableMixin - Text selection and cursor handling
  • DefaultSelectableMixin - Default selection behaviors
  • BlockComponentConfigurable - Access to configuration
  • BlockComponentBackgroundColorMixin - Background color styling
  • NestedBlockComponentStatefulWidgetMixin - Support for nested children
  • BlockComponentTextDirectionMixin - LTR/RTL text direction
  • BlockComponentAlignMixin - Text alignment (left, center, right)
Source: /lib/src/editor/block_component/paragraph_block_component/paragraph_block_component.dart:89

Placeholder Handling

The paragraph block shows a placeholder when empty:
void _onSelectionChange() {
  final selection = editorState.selection;

  if (widget.showPlaceholder != null) {
    setState(() {
      _showPlaceholder = widget.showPlaceholder!(editorState, node);
    });
  } else {
    final showPlaceholder = selection != null &&
        (selection.isSingle && selection.start.path.equals(node.path));
    if (showPlaceholder != _showPlaceholder) {
      setState(() => _showPlaceholder = showPlaceholder);
    }
  }
}
Source: /lib/src/editor/block_component/paragraph_block_component/paragraph_block_component.dart:131

Rich Text Rendering

Paragraphs use AppFlowyRichText for rendering:
AppFlowyRichText(
  key: forwardKey,
  delegate: this,
  node: widget.node,
  editorState: editorState,
  textAlign: alignment?.toTextAlign ?? textAlign,
  placeholderText: _showPlaceholder ? placeholderText : ' ',
  textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
    textStyleWithTextSpan(textSpan: textSpan),
  ),
  placeholderTextSpanDecorator: (textSpan) => textSpan.updateTextStyle(
    placeholderTextStyleWithTextSpan(textSpan: textSpan),
  ),
  textDirection: textDirection,
  cursorColor: editorState.editorStyle.cursorColor,
  selectionColor: editorState.editorStyle.selectionColor,
  cursorWidth: editorState.editorStyle.cursorWidth,
)
Source: /lib/src/editor/block_component/paragraph_block_component/paragraph_block_component.dart:165

Using in Editor

Add paragraph support to your editor:
AppFlowyEditor(
  editorState: editorState,
  blockComponentBuilders: {
    ParagraphBlockKeys.type: ParagraphBlockComponentBuilder(
      configuration: standardBlockComponentConfiguration.copyWith(
        placeholderText: (_) => 'Start typing...',
      ),
    ),
    // ... other block types
  },
);

Inserting Paragraphs

// Insert a new paragraph
final transaction = editorState.transaction;
transaction.insertNode(
  [0], // path
  paragraphNode(text: 'New paragraph'),
);
editorState.apply(transaction);

// Insert after current selection
final selection = editorState.selection;
if (selection != null) {
  final transaction = editorState.transaction;
  transaction.insertNode(
    selection.end.path.next,
    paragraphNode(text: 'Inserted paragraph'),
  );
  editorState.apply(transaction);
}

Customizing Text Style

Override the text style configuration:
ParagraphBlockComponentBuilder(
  configuration: BlockComponentConfiguration(
    textStyle: (node, {textSpan}) {
      // Check for custom attributes
      final fontSize = node.attributes['fontSize'] as double? ?? 16.0;
      final fontFamily = node.attributes['fontFamily'] as String?;
      
      return TextStyle(
        fontSize: fontSize,
        fontFamily: fontFamily,
        height: 1.5,
      );
    },
  ),
)

Key Features

  • Rich Text Support - Full Delta format with inline styling
  • Text Direction - LTR and RTL support
  • Text Alignment - Left, center, right, justify
  • Background Color - Custom background colors
  • Placeholder Text - Configurable empty state text
  • Nested Children - Can contain child blocks
  • Selection Handling - Full cursor and selection support
  • Actions - Drag handles and custom actions

Heading Blocks

Learn about heading blocks

Rich Text API

AppFlowyRichText API reference

Build docs developers (and LLMs) love