quill_web_editor 1.0.1 copy "quill_web_editor: ^1.0.1" to clipboard
quill_web_editor: ^1.0.1 copied to clipboard

A rich text editor package for Flutter Web powered by Quill.js. Provides full-featured editing with tables, media embedding, custom fonts, and HTML export.

Quill Web Editor #

A powerful, full-featured rich text editor package for Flutter Web powered by Quill.js.

Flutter Quill.js License: MIT

Quill Logo

✨ Features #

Feature Description
πŸ“ Rich Text Editing Full formatting toolbar with bold, italic, underline, strikethrough
πŸ“Š Table Support Create and edit tables with quill-table-better
πŸ–ΌοΈ Media Embedding Images, videos, and iframes with resize controls
🎨 Custom Fonts Roboto, Open Sans, Lato, Montserrat, Crimson Pro, DM Sans, Source Code Pro
πŸ“ Font Sizes Small, Normal, Large, Huge
πŸ“ Line Heights 1.0, 1.5, 2.0, 2.5, 3.0
πŸ”— Links & Code Hyperlinks, blockquotes, inline code, code blocks
πŸ“‹ Smart Paste Preserves fonts and formatting when pasting
πŸ’Ύ HTML Export Clean HTML export with all styles preserved
πŸ” Preview Full-screen preview with print support
πŸ”Ž Zoom Controls 50% to 300% zoom range
πŸ˜€ Emoji Support Built-in emoji picker
βœ… Checklists Task lists with checkboxes
🎯 Alignment Left, center, right, justify

πŸ“¦ Installation #

Step 1: Add Dependency #

Add to your pubspec.yaml:

dependencies:
  quill_web_editor:
    git:
      url: https://github.com/icici/quill_web_editor.git

Step 2: Copy Web Assets #

Copy the HTML files from the package to your app's web/ directory:

your_app/
└── web/
    β”œβ”€β”€ index.html
    β”œβ”€β”€ quill_editor.html    ← Copy from package
    └── quill_viewer.html    ← Copy from package

Step 3: Import #

import 'package:quill_web_editor/quill_web_editor.dart';

πŸš€ Quick Start #

Basic Editor #

import 'package:flutter/material.dart';
import 'package:quill_web_editor/quill_web_editor.dart';

class MyEditor extends StatefulWidget {
  const MyEditor({super.key});

  @override
  State<MyEditor> createState() => _MyEditorState();
}

class _MyEditorState extends State<MyEditor> {
  final GlobalKey<QuillEditorWidgetState> _editorKey = GlobalKey();
  String _currentHtml = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: QuillEditorWidget(
        key: _editorKey,
        onContentChanged: (html, delta) {
          setState(() => _currentHtml = html);
        },
        initialHtml: '<p>Start writing...</p>',
      ),
    );
  }
}

Read-Only Viewer #

QuillEditorWidget(
  readOnly: true,
  initialHtml: '<p>This content is read-only</p>',
)

πŸ“– API Reference #

For detailed developer documentation, including architecture, feature deep-dives, and extension guides, see the Developer Guide.

QuillEditorWidget #

The main editor widget that embeds Quill.js via an iframe.

Constructor Parameters

Parameter Type Default Description
key GlobalKey<QuillEditorWidgetState> - Key for accessing editor state
width double? null Editor width (expands to fill if null)
height double? null Editor height (expands to fill if null)
onContentChanged Function(String html, dynamic delta)? null Callback when content changes
onReady VoidCallback? null Callback when editor is initialized
readOnly bool false Enable viewer mode
initialHtml String? null Initial HTML content
initialDelta dynamic null Initial Quill Delta content
placeholder String? null Placeholder text
editorHtmlPath String? 'quill_editor.html' Custom editor HTML path
viewerHtmlPath String? 'quill_viewer.html' Custom viewer HTML path

State Methods

Access via GlobalKey<QuillEditorWidgetState>:

final GlobalKey<QuillEditorWidgetState> _editorKey = GlobalKey();

// Later...
_editorKey.currentState?.methodName();
Method Description
setHTML(String html, {bool replace = true}) Set editor content from HTML
insertHtml(String html, {bool replace = false}) Insert HTML at cursor position
setContents(dynamic delta) Set content from Quill Delta
insertText(String text) Insert plain text at cursor
getContents() Request current content (triggers callback)
clear() Clear all editor content
focus() Focus the editor
undo() Undo the last operation
redo() Redo the last undone operation
format(String format, dynamic value) Apply formatting to selection
insertTable(int rows, int cols) Insert a table at cursor
zoomIn() Increase zoom by 10%
zoomOut() Decrease zoom by 10%
resetZoom() Reset zoom to 100%
setZoom(double level) Set specific zoom level (0.5 - 3.0)

Properties

Property Type Description
currentZoom double Current zoom level (1.0 = 100%)
isReady bool Whether editor is ready for commands

🧩 Components #

SaveStatusIndicator #

Displays save status with animated transitions.

SaveStatusIndicator(status: SaveStatus.saved)
SaveStatusIndicator(status: SaveStatus.saving)
SaveStatusIndicator(status: SaveStatus.unsaved)

ZoomControls #

Zoom in/out controls with percentage display.

ZoomControls(
  zoomLevel: _zoomLevel,
  onZoomIn: () => _editorKey.currentState?.zoomIn(),
  onZoomOut: () => _editorKey.currentState?.zoomOut(),
  onReset: () => _editorKey.currentState?.resetZoom(),
)

OutputPreview #

Tabbed preview showing HTML source and plain text.

OutputPreview(html: _currentHtml)

StatCard & StatCardRow #

Display document statistics.

// Single stat
StatCard(label: 'Words', value: '150')

// Multiple stats in a row
StatCardRow(
  stats: [
    (label: 'Words', value: '150', icon: Icons.text_fields),
    (label: 'Characters', value: '890', icon: Icons.format_size),
  ],
)

AppCard #

Styled container card with optional title.

AppCard(
  title: 'Document Info',  // Optional
  child: YourContent(),
)

HtmlPreviewDialog #

Full-screen HTML preview dialog with copy and print options.

// Show preview dialog
HtmlPreviewDialog.show(context, htmlContent);

InsertHtmlDialog #

Dialog for inserting raw HTML into the editor.

final result = await InsertHtmlDialog.show(context);
if (result != null) {
  _editorKey.currentState?.insertHtml(
    result.html,
    replace: result.replaceContent,
  );
}

πŸ› οΈ Services #

DocumentService #

Utility service for document operations.

Download Files

// Download as HTML (with styles)
DocumentService.downloadHtml(
  htmlContent,
  filename: 'document.html',
  cleanHtml: true,  // Remove editor artifacts
);

// Download as plain text
DocumentService.downloadText(
  textContent,
  filename: 'document.txt',
);

Clipboard Operations

// Copy to clipboard
final success = await DocumentService.copyToClipboard(text);

// Read from clipboard
final text = await DocumentService.readFromClipboard();

Print

// Opens print-ready document in new tab
DocumentService.printHtml(htmlContent);

Local Storage

// Save draft
DocumentService.saveToLocalStorage('my-draft', htmlContent);

// Load draft
final draft = DocumentService.loadFromLocalStorage('my-draft');

// Check if exists
if (DocumentService.hasLocalStorage('my-draft')) {
  // ...
}

// Remove
DocumentService.removeFromLocalStorage('my-draft');

πŸ”§ Utilities #

HtmlCleaner #

Process HTML for export.

// Clean editor artifacts (selection classes, data attributes)
final clean = HtmlCleaner.cleanForExport(dirtyHtml);

// Extract plain text from HTML
final text = HtmlCleaner.extractText(html);

// Check if content is empty
if (HtmlCleaner.isEmpty(html)) {
  print('No content');
}

// Normalize color to hex
final hex = HtmlCleaner.normalizeColor('rgb(255, 0, 0)');  // '#ff0000'

TextStats #

Calculate document statistics.

final stats = TextStats.fromHtml(html);

print('Words: ${stats.wordCount}');
print('Characters: ${stats.charCount}');
print('Characters (no spaces): ${stats.charCountNoSpaces}');
print('Paragraphs: ${stats.paragraphCount}');
print('Sentences: ${stats.sentenceCount}');

ExportStyles #

Generate CSS for exported HTML.

// Get full CSS string
final css = ExportStyles.fullCss;

// Generate complete HTML document
final fullHtml = ExportStyles.generateHtmlDocument(
  content,
  title: 'My Document',  // Optional
);

🎨 Theming #

Using Built-in Theme #

MaterialApp(
  theme: AppTheme.lightTheme,
  home: MyApp(),
)

Color Palette #

AppColors.accent        // Primary accent color (#C45D35)
AppColors.surface       // Card/surface color (white)
AppColors.background    // Scaffold background
AppColors.textPrimary   // Primary text color
AppColors.textSecondary // Secondary text color
AppColors.border        // Border color
AppColors.success       // Success state color
AppColors.warning       // Warning state color
AppColors.error         // Error state color

Text Styles #

AppTheme.serifTextStyle  // Crimson Pro, 18px
AppTheme.sansTextStyle   // DM Sans, 14px
AppTheme.monoTextStyle   // Source Code Pro, 14px

Decorations #

Container(
  decoration: AppTheme.editorContainerDecoration,
  child: QuillEditorWidget(...),
)

Container(
  decoration: AppTheme.cardDecoration,
  child: YourContent(),
)

βš™οΈ Configuration #

EditorConfig #

// Zoom settings
EditorConfig.minZoom         // 0.5 (50%)
EditorConfig.maxZoom         // 3.0 (300%)
EditorConfig.defaultZoom     // 1.0 (100%)
EditorConfig.zoomStep        // 0.1 (10%)

// Table defaults
EditorConfig.defaultTableRows  // 3
EditorConfig.defaultTableCols  // 3
EditorConfig.maxTableRows      // 20
EditorConfig.maxTableCols      // 10

// Timing
EditorConfig.contentChangeThrottleMs  // 200ms
EditorConfig.autoSaveDebounceMs       // 500ms

AppFonts #

// Available fonts
AppFonts.availableFonts  // List<FontConfig>

// Available sizes
AppFonts.availableSizes  // [small, normal, large, huge]

// Available line heights
AppFonts.availableLineHeights  // [1.0, 1.5, 2.0, 2.5, 3.0]

πŸ“ Project Structure #

lib/
β”œβ”€β”€ quill_web_editor.dart              # Main export file
└── src/
    β”œβ”€β”€ core/
    β”‚   β”œβ”€β”€ constants/
    β”‚   β”‚   β”œβ”€β”€ app_colors.dart        # Color palette
    β”‚   β”‚   β”œβ”€β”€ app_fonts.dart         # Font configurations
    β”‚   β”‚   └── editor_config.dart     # Editor settings
    β”‚   β”œβ”€β”€ theme/
    β”‚   β”‚   └── app_theme.dart         # Theme data
    β”‚   └── utils/
    β”‚       β”œβ”€β”€ html_cleaner.dart      # HTML processing
    β”‚       β”œβ”€β”€ text_stats.dart        # Document statistics
    β”‚       └── export_styles.dart     # Export CSS generation
    β”œβ”€β”€ widgets/
    β”‚   β”œβ”€β”€ quill_editor_widget.dart   # Main editor widget
    β”‚   β”œβ”€β”€ save_status_indicator.dart # Save status display
    β”‚   β”œβ”€β”€ zoom_controls.dart         # Zoom UI
    β”‚   β”œβ”€β”€ output_preview.dart        # HTML/text preview
    β”‚   β”œβ”€β”€ stat_card.dart             # Statistics cards
    β”‚   β”œβ”€β”€ app_card.dart              # Styled container
    β”‚   β”œβ”€β”€ html_preview_dialog.dart   # Preview dialog
    β”‚   └── insert_html_dialog.dart    # HTML insertion dialog
    └── services/
        └── document_service.dart      # Document operations

web/
β”œβ”€β”€ quill_editor.html                  # Editor HTML (full-featured)
└── quill_viewer.html                  # Viewer HTML (read-only)

πŸ§ͺ Testing #

The package includes comprehensive tests. Run them with:

flutter test

Testing with Google Fonts #

The package uses Google Fonts which require special setup for testing. Font files are bundled in test/fonts/ and configured in flutter_test_config.dart.


πŸ’‘ Examples #

Complete Editor with Sidebar #

class EditorPage extends StatefulWidget {
  @override
  State<EditorPage> createState() => _EditorPageState();
}

class _EditorPageState extends State<EditorPage> {
  final _editorKey = GlobalKey<QuillEditorWidgetState>();
  String _html = '';
  double _zoom = 1.0;
  SaveStatus _status = SaveStatus.saved;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Editor'),
        actions: [
          // Undo/Redo
          IconButton(
            onPressed: () => _editorKey.currentState?.undo(),
            icon: Icon(Icons.undo),
            tooltip: 'Undo',
          ),
          IconButton(
            onPressed: () => _editorKey.currentState?.redo(),
            icon: Icon(Icons.redo),
            tooltip: 'Redo',
          ),
          SizedBox(width: 8),
          // Zoom
          ZoomControls(
            zoomLevel: _zoom,
            onZoomIn: () {
              _editorKey.currentState?.zoomIn();
              setState(() => _zoom += 0.1);
            },
            onZoomOut: () {
              _editorKey.currentState?.zoomOut();
              setState(() => _zoom -= 0.1);
            },
            onReset: () {
              _editorKey.currentState?.resetZoom();
              setState(() => _zoom = 1.0);
            },
          ),
          SaveStatusIndicator(status: _status),
          FilledButton.icon(
            onPressed: () => DocumentService.downloadHtml(_html),
            icon: Icon(Icons.save),
            label: Text('Save'),
          ),
        ],
      ),
      body: Row(
        children: [
          // Editor
          Expanded(
            child: Container(
              margin: EdgeInsets.all(24),
              decoration: AppTheme.editorContainerDecoration,
              child: ClipRRect(
                borderRadius: BorderRadius.circular(16),
                child: QuillEditorWidget(
                  key: _editorKey,
                  onContentChanged: (html, delta) {
                    setState(() {
                      _html = html;
                      _status = SaveStatus.unsaved;
                    });
                  },
                ),
              ),
            ),
          ),
          // Sidebar
          SizedBox(
            width: 320,
            child: Padding(
              padding: EdgeInsets.all(24),
              child: Column(
                children: [
                  AppCard(
                    title: 'Statistics',
                    child: StatCardRow(
                      stats: [
                        (
                          label: 'Words',
                          value: TextStats.fromHtml(_html).wordCount.toString(),
                          icon: null,
                        ),
                      ],
                    ),
                  ),
                  SizedBox(height: 16),
                  Expanded(
                    child: AppCard(
                      title: 'Preview',
                      child: OutputPreview(html: _html),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Auto-Save Implementation #

Timer? _saveTimer;

void _onContentChanged(String html, dynamic delta) {
  setState(() => _status = SaveStatus.unsaved);
  
  _saveTimer?.cancel();
  _saveTimer = Timer(Duration(milliseconds: 500), () {
    setState(() => _status = SaveStatus.saving);
    
    // Save to backend or local storage
    DocumentService.saveToLocalStorage('draft', html);
    
    setState(() => _status = SaveStatus.saved);
  });
}

πŸ”— Running the Example #

cd example
flutter run -d chrome

πŸš€ Deployment #

For production deployments, you can host the HTML files on a CDN or static hosting service and reference them via editorHtmlPath and viewerHtmlPath parameters.

Quick Setup #

QuillEditorWidget(
  editorHtmlPath: 'https://your-cdn.com/quill-editor/quill_editor.html',
  viewerHtmlPath: 'https://your-cdn.com/quill-editor/quill_viewer.html',
  onContentChanged: (html, delta) {
    // Handle changes
  },
)

Files to Host #

  • quill_editor.html - Main editor HTML
  • quill_viewer.html - Viewer HTML
  • js/ folder - Custom JavaScript overrides
  • styles/ folder - Custom CSS (e.g., mulish-font.css)
  • fonts/ folder - Font files (optional, if hosting locally)

See DEPLOYMENT.md for detailed deployment instructions, folder structure, and hosting configuration.


πŸ“„ License #

MIT License - see LICENSE file for details.


🀝 Contributing #

Contributions are welcome! Please read our contributing guidelines before submitting PRs.


Made with ❀️ using Flutter & Quill.js

2
likes
0
points
285
downloads

Publisher

unverified uploader

Weekly Downloads

A rich text editor package for Flutter Web powered by Quill.js. Provides full-featured editing with tables, media embedding, custom fonts, and HTML export.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, google_fonts, pointer_interceptor

More

Packages that depend on quill_web_editor