Skip to main content

Overview

Quote blocks are used to visually distinguish quoted text, citations, or emphasized content from regular paragraphs. They feature a distinctive vertical bar and optional custom styling.

Block Keys

class QuoteBlockKeys {
  static const String type = 'quote';
  static const String delta = blockComponentDelta;
  static const String backgroundColor = blockComponentBackgroundColor;
  static const String textDirection = blockComponentTextDirection;
}
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:6

Creating Quote Nodes

Use the quoteNode() helper function:
// Simple quote
final quote = quoteNode(
  delta: Delta()..insert('To be or not to be...'),
);

// Quote with rich text
final richQuote = quoteNode(
  delta: Delta()
    ..insert('Important: ')
    ..insert('This is emphasized', attributes: {'italic': true}),
);

// RTL quote
final rtlQuote = quoteNode(
  delta: Delta()..insert('اقتباس مهم'),
  textDirection: 'rtl',
);

// Quote with custom attributes
final styledQuote = quoteNode(
  delta: Delta()..insert('Custom styled quote'),
  attributes: {
    'backgroundColor': '0xFFF5F5F5',
  },
);

// Quote with nested children
final nestedQuote = quoteNode(
  delta: Delta()..insert('Main quote'),
  children: [
    paragraphNode(text: '— Author Name'),
  ],
);
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:18

Component Builder

Create a quote component with custom configuration:
final quoteBuilder = QuoteBlockComponentBuilder(
  configuration: BlockComponentConfiguration(
    padding: (node) => const EdgeInsets.symmetric(
      vertical: 8.0,
      horizontal: 0.0,
    ),
    placeholderText: (node) => 'Quote',
    textStyle: (node, {textSpan}) => const TextStyle(
      fontSize: 16.0,
      fontStyle: FontStyle.italic,
      color: Colors.grey[700],
    ),
  ),
  iconBuilder: (context, node) {
    // Custom quote icon/bar
    return Container(
      alignment: Alignment.center,
      constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
      padding: const EdgeInsets.only(right: 4.0),
      child: Container(
        width: 4,
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(2),
        ),
      ),
    );
  },
);
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:36

Default Quote Icon

The default quote indicator is a vertical bar:
class _QuoteIcon extends StatelessWidget {
  const _QuoteIcon();

  @override
  Widget build(BuildContext context) {
    final textScaleFactor =
        context.read<EditorState>().editorStyle.textScaleFactor;

    return Container(
      alignment: Alignment.center,
      constraints:
          const BoxConstraints(minWidth: 26, minHeight: 22) * textScaleFactor,
      padding: const EdgeInsets.only(right: 4.0),
      child: Container(
        width: 4 * textScaleFactor,
        color: '#00BCF0'.tryToColor(),  // Light blue color
      ),
    );
  }
}
The default bar:
  • Width: 4px
  • Color: #00BCF0 (cyan/light blue)
  • Min width: 26px
  • Min height: 22px
  • Right padding: 4px
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:192

Widget Structure

class QuoteBlockComponentWidget extends BlockComponentStatefulWidget {
  const QuoteBlockComponentWidget({
    super.key,
    required super.node,
    super.showActions,
    super.actionBuilder,
    super.actionTrailingBuilder,
    super.configuration = const BlockComponentConfiguration(),
    this.iconBuilder,
  });

  /// Custom icon builder for the quote indicator
  final BlockIconBuilder? iconBuilder;
}
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:69

Mixins Used

class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
    with
        SelectableMixin,
        DefaultSelectableMixin,
        BlockComponentConfigurable,
        BlockComponentBackgroundColorMixin,
        BlockComponentTextDirectionMixin,
        BlockComponentAlignMixin
Functionality:
  • Text selection and cursor handling
  • Background color support
  • Text direction (LTR/RTL)
  • Text alignment
Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:87

Layout Structure

Quotes use an IntrinsicHeight widget to stretch the vertical bar:
Widget child = Container(
  width: double.infinity,
  alignment: alignment,
  child: IntrinsicHeight(
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      mainAxisAlignment: MainAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      textDirection: textDirection,
      children: [
        widget.iconBuilder != null
            ? widget.iconBuilder!(context, node)
            : const _QuoteIcon(),
        Flexible(
          child: AppFlowyRichText(
            // ... rich text configuration
          ),
        ),
      ],
    ),
  ),
);
The IntrinsicHeight ensures the vertical bar extends to match the text height, even for multi-line quotes. Source: /lib/src/editor/block_component/quote_block_component/quote_block_component.dart:121

Markdown Shortcut

Create quotes using markdown syntax:
> This becomes a quote block
Type > followed by a space to create a quote block.
// Enable in editor:
characterShortcutEvents: [
  formatDoubleQuoteToQuote,
  // ... other shortcuts
],

Using in Editor

Add quote support to your editor:
AppFlowyEditor(
  editorState: editorState,
  blockComponentBuilders: {
    QuoteBlockKeys.type: QuoteBlockComponentBuilder(
      configuration: standardBlockComponentConfiguration.copyWith(
        placeholderText: (_) => 'Quote',
        textStyle: (node, {textSpan}) => const TextStyle(
          fontStyle: FontStyle.italic,
        ),
      ),
    ),
    // ... other block types
  },
  characterShortcutEvents: [
    formatDoubleQuoteToQuote,
    // ... other shortcuts
  ],
);

Inserting Quotes

// Insert a quote block
final transaction = editorState.transaction;
transaction.insertNode(
  path,
  quoteNode(
    delta: Delta()..insert('Quoted text here'),
  ),
);
editorState.apply(transaction);

// Convert paragraph to quote
final selection = editorState.selection;
if (selection != null) {
  final node = editorState.getNodeAtPath(selection.start.path);
  if (node != null && node.type == ParagraphBlockKeys.type) {
    final transaction = editorState.transaction;
    transaction.updateNode(node, {
      'type': QuoteBlockKeys.type,
    });
    editorState.apply(transaction);
  }
}

Custom Quote Styles

Gradient Bar

QuoteBlockComponentBuilder(
  iconBuilder: (context, node) {
    return Container(
      alignment: Alignment.center,
      constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
      padding: const EdgeInsets.only(right: 8.0),
      child: Container(
        width: 5,
        decoration: BoxDecoration(
          gradient: const LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFF00BCF0),
              Color(0xFF6C5CE7),
            ],
          ),
          borderRadius: BorderRadius.circular(2.5),
        ),
      ),
    );
  },
)

Quote Mark Icon

QuoteBlockComponentBuilder(
  iconBuilder: (context, node) {
    return Container(
      constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
      padding: const EdgeInsets.only(right: 8.0),
      child: const Icon(
        Icons.format_quote,
        size: 20,
        color: Colors.grey,
      ),
    );
  },
)

Background Style

QuoteBlockComponentBuilder(
  configuration: BlockComponentConfiguration(
    padding: (node) => const EdgeInsets.all(16.0),
    textStyle: (node, {textSpan}) => TextStyle(
      fontSize: 16.0,
      fontStyle: FontStyle.italic,
      color: Colors.grey[800],
    ),
  ),
  iconBuilder: (context, node) {
    return Container(
      constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
      padding: const EdgeInsets.only(right: 8.0),
      child: Container(
        width: 4,
        decoration: BoxDecoration(
          color: Colors.blue,
          borderRadius: BorderRadius.circular(2),
        ),
      ),
    );
  },
)

// Set background color on the node:
quoteNode(
  delta: Delta()..insert('Styled quote'),
  attributes: {
    QuoteBlockKeys.backgroundColor: '0xFFF8F9FA',
  },
)

Author Attribution

Create quotes with attribution using nested nodes:
final quoteWithAuthor = quoteNode(
  delta: Delta()..insert(
    'The only way to do great work is to love what you do.',
  ),
  children: [
    paragraphNode(
      text: '— Steve Jobs',
      attributes: {
        'textAlign': 'right',
      },
    ),
  ],
);

Colored Quotes

QuoteBlockComponentBuilder(
  configuration: BlockComponentConfiguration(
    textStyle: (node, {textSpan}) {
      // Get quote type from attributes
      final quoteType = node.attributes['quoteType'] ?? 'default';
      final colors = {
        'info': Colors.blue[700],
        'warning': Colors.orange[700],
        'error': Colors.red[700],
        'default': Colors.grey[700],
      };
      
      return TextStyle(
        fontStyle: FontStyle.italic,
        color: colors[quoteType],
      );
    },
  ),
  iconBuilder: (context, node) {
    final quoteType = node.attributes['quoteType'] ?? 'default';
    final colors = {
      'info': Colors.blue,
      'warning': Colors.orange,
      'error': Colors.red,
      'default': const Color(0xFF00BCF0),
    };
    
    return Container(
      alignment: Alignment.center,
      constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
      padding: const EdgeInsets.only(right: 4.0),
      child: Container(
        width: 4,
        color: colors[quoteType],
      ),
    );
  },
)

Multi-line Quotes

Quotes automatically handle multi-line content:
final longQuote = quoteNode(
  delta: Delta()..insert(
    'This is a longer quote that spans multiple lines. '
    'The vertical bar will automatically extend to match '
    'the full height of the text content.',
  ),
);
The IntrinsicHeight widget ensures the vertical bar stretches to match the text height.

Key Features

  • Visual Distinction - Vertical bar for clear identification
  • Rich Text Support - Full Delta format with inline styling
  • Custom Icons - Replaceable quote indicator
  • Text Direction - LTR and RTL support
  • Alignment - Left, center, right alignment
  • Background Color - Custom background colors
  • Markdown Shortcut - > syntax support
  • Nested Content - Can contain child blocks
  • Automatic Height - Bar scales with content

Use Cases

  1. Block Quotes - Citations from external sources
  2. Pull Quotes - Highlight key passages
  3. Testimonials - Customer or user feedback
  4. Warnings - Important notices (with custom styling)
  5. Tips - Helpful information (with custom styling)
  6. Code Comments - Explanatory notes about code

Best Practices

  1. Use sparingly - Too many quotes reduce impact
  2. Add attribution - Credit sources when quoting
  3. Keep concise - Long quotes are hard to scan
  4. Style consistently - Use similar styling throughout
  5. Consider semantics - Use appropriate semantic meaning

Accessibility

  • Quote blocks are properly structured for screen readers
  • Text direction is respected for RTL languages
  • Selection and navigation work as expected
  • Keyboard shortcuts follow standard conventions

Paragraph Blocks

Basic text blocks

Heading Blocks

Document structure with headings

Build docs developers (and LLMs) love