paste_anything_editor

A rich text editor for Flutter web that keeps formatting when you copy and paste from Word, Excel, Google Docs, web pages, Markdown, tables, math formulas and images.

It is built on flutter_quill and works with plain HTML strings: load HTML in, get HTML out.

Web only. Rich copy/paste uses the browser Clipboard API.

Have your own editor? Use paste_anything, the copy-paste core of this package, with no Flutter or Quill dependency.

Editor with pasted formulas, HTML preview and the docked math keyboard showing formula templates

Features

  • Paste with formatting (Ctrl/Cmd+V or the paste button): bold, italic, underline, colors, fonts, headings, lists, links and tables from Word, Excel, Google Docs, web pages, HTML source and Markdown.
  • Copy with formatting: other editors receive formatted HTML.
  • Math: a docked math keyboard with symbols and formula templates, LaTeX formulas rendered as equations, and paste from KaTeX, MathJax, MathML or $...$ text.
  • Images (opt-in): screenshots, "Copy image" and images inside pasted HTML.

Install

flutter pub add paste_anything_editor

A runnable demo lives in example/:

cd example && flutter run -d chrome

Quick start

import 'package:paste_anything_editor/paste_anything_editor.dart';

final controller = PaeEditorController();

PaeEditor(
  controller: controller,
  initialText: '<p>Hello</p>',
  onChanged: (html) => print(html),
);

final html = await controller.getText(); // save
controller.setText(html);                // load

Math keyboard

The ∑ toolbar button opens a math keyboard docked at the bottom of the screen. The editor keeps focus, so you can click anywhere in the text to move the cursor and keep tapping symbols, like an on-screen keyboard.

  • Symbol tabs insert Unicode characters (operators, Greek, sets, arrows, ...).
  • The Formulas tab inserts LaTeX templates such as fractions and roots. Click an inserted formula to edit it.
  • LaTeX editor opens a dialog to type any formula with a live preview.
  • Press ∑ again, or the hide button, to close it.

LaTeX editor dialog with formula templates, live preview and symbol palette

Prefer a modal dialog instead?

PaeEditor(
  controller: controller,
  mathInputMode: PaeMathInputMode.dialog,
);

Formulas are saved as <span class="math-tex" data-latex="...">\(...\)</span>, which KaTeX and MathJax can render. To show saved HTML with formulas in HtmlWidget:

HtmlWidget(html, customWidgetBuilder: paeMathHtmlBuilder);

Images (opt-in)

Off by default. Clipboard images are ignored until you turn them on on the controller. When enabled with no upload handler, pasted bitmaps become inline data: URIs (base64 “bytecode” in the HTML).

// 1) Enable + embed as data: URIs (no server).
final controller = PaeEditorController(allowImagePaste: true);

// 2) Enable + upload (return a URL, or null to drop the image).
final controller = PaeEditorController(
  allowImagePaste: true,
  onImagePaste: (bytes, mimeType) async {
    return await uploadAndGetUrl(bytes, mimeType);
  },
);

PaeEditor(controller: controller);

Same flag exists on the core paster if you use paste_anything alone:

const PaePaster(allowImages: true).pasteInto(myTarget);

Full customization (colors, sizes, toasts)

Developers get three layers:

Layer What it styles
PaeEditorTheme / PaeEditorLabels Editor frame, copy/paste row, all toasts
PaeMathTheme / PaeMathLabels Math keyboard + LaTeX dialog
onNotify Replace SnackBars with your own toast system
PaeEditor(
  controller: controller,
  // Own toast system (optional). When set, default SnackBars are skipped.
  onNotify: (message, {isError = false}) {
    showMyToast(message, error: isError);
  },
  editorTheme: PaeEditorTheme(
    borderRadius: 12,
    borderColor: Colors.black26,
    snackBarBehavior: SnackBarBehavior.floating,
    snackBarDuration: Duration(seconds: 2),
    snackBarBackgroundColor: Color(0xFF1E293B),
    copyFormattedColor: Colors.indigo,
  ),
  editorLabels: const PaeEditorLabels(
    contentLabel: 'Body',
    pastedWithFormatting: 'Paste OK',
    cannotFormatFormula: 'LaTeX can’t be bolded — edit the formula instead',
  ),
  mathTheme: PaeMathTheme(
    seedColor: Colors.teal,
    dialogBorderRadius: 16,
    dialogMaxWidth: 640,
    keyboardHeight: 280,
  ),
  mathLabels: const PaeMathLabels(
    title: 'Equations',
    openLatexEditor: 'Open equation editor',
  ),
);

App-wide:

MaterialApp(
  theme: ThemeData(
    extensions: const [
      PaeEditorTheme(seedColor: Colors.indigo),
      PaeMathTheme(seedColor: Colors.teal),
    ],
  ),
);

Customize math colors and texts

By default the keyboard and dialog follow your app's Material ColorScheme. Set a PaeMathTheme app-wide:

MaterialApp(
  theme: ThemeData(
    extensions: const [PaeMathTheme(seedColor: Colors.teal)],
  ),
);

or for one editor, together with your own texts:

PaeEditor(
  controller: controller,
  mathTheme: PaeMathTheme(
    headerColor: const Color(0xFF1E3A8A),
    headerForegroundColor: Colors.white,
    keyboardHeight: 320,
    // Dialog shape / chrome
    dialogBorderRadius: 16,
    dialogElevation: 12,
    dialogMaxWidth: 640,
    dialogShape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(16),
      side: const BorderSide(color: Color(0x22000000)),
    ),
    showHeaderIcon: true,
  ),
  mathLabels: const PaeMathLabels(
    title: 'Equations',
    keyboardTitle: 'Equations',
  ),
);

Every color, radius, shape and size is optional; unset values fall back to the theme. Prefer dialogShape for full control (corners, border); or set dialogBorderRadius / dialogBorderSide alone. The screenshot above uses exactly this navy header and the 'Equations' title.

Math without the editor

Use the pieces on their own:

// Docked keyboard in your own layout.
PaeMathKeyboard(onSymbol: (char) => ..., onFormula: (latex) => ...);

// Modal dialog.
final result = await PaeMathDialog.show(context);
switch (result) {
  case PaeMathSymbolInsertion(:final char): ...
  case PaeMathFormulaInsertion(:final latex): ...
  case null: break;
}

// Render a formula.
PaeMathView(r'\frac{a}{b}');

Use only the paste logic

This package re-exports paste_anything, so its pieces are available here too: PaeClipboard, PaeHtml.normalize, PaeMath and PaePaster. PaeEditorController implements its PaeTarget interface. If you don't need this editor, depend on paste_anything alone.

Upgrading from 0.3

Names now start with Pae (RichHtmlEditor is now PaeEditor, MathPanelTheme is now PaeMathTheme, ...). The old names still work but are deprecated. One breaking change: controller.insertImage(url) is now controller.insertImageUrl(url).

Good to know

  • The first rich paste shows the browser's clipboard permission prompt. The editor waits for the page to regain focus and then pastes.
  • Word's local file:/// images can't be read by a browser. Remote images need CORS to display.
  • Needs Flutter 3.27 or newer. On Flutter 3.44 or newer, flutter_quill must be 11.6.0 or newer (flutter pub upgrade flutter_quill).
  • flutter_math_fork 0.7.4 needs Flutter 3.35 or newer. On older Flutter, pin flutter_math_fork: 0.7.3 in your app.

License

MIT, see LICENSE.

Libraries

paste_anything_editor
Rich text editor for Flutter web with copy/paste that keeps formatting (Word, Excel, Google Docs, Markdown, HTML, tables, math formulas, images) and a math keyboard. Loads and saves HTML.