live_markdown_editor 0.5.3
live_markdown_editor: ^0.5.3 copied to clipboard
A source-preserving Flutter Markdown editor with raw, live, and read modes.
live_markdown_editor #
A source-preserving, Obsidian-inspired Markdown editor for Flutter with three modes — Raw (literal source), Live (formatted with hidden delimiters, revealed at caret/selection), and Read (delimiter-free, selectable).
The editor is package-owned: its custom editable engine uses only
Flutter foundation, services, widgets, and rendering layers (zero Material or
Cupertino dependencies in lib/). On desktop and mobile, selection menus are
the platform's own native system menus; on web, the browser owns
selection and context menus natively.
Supported Platforms #
| Platform | Selection Menu Mechanism | Native Menu | Manually Tested |
|---|---|---|---|
| Android | Floating ActionMode toolbar |
Yes | ✅ |
| Linux | GTK popover menu (GtkMenu) |
Yes | ✅ |
| Web | Browser-owned DOM selection & context menu | Browser | ✅ |
| iOS | UIEditMenuInteraction / UIMenuController |
Yes | ⏳ |
| macOS | Native NSMenu popup |
Yes | ⏳ |
| Windows | Win32 TrackPopupMenuEx |
Yes | ⏳ |
Note: On mobile and desktop, native menus never render under the keyboard or off-screen. On Web, the editor coordinates with the browser engine's text input connection for genuine browser-native context menus and copy/paste.
Supported Markdown Syntax #
| Syntax | Markdown Pattern | Live Mode Presentation |
|---|---|---|
| Paragraphs | Plain text with words |
Preserves source, wraps naturally |
| Headings (H1–H6) | # H1 through ###### H6 |
Styled heading scales; # revealed at caret |
| Bold | **bold** or __bold__ |
Bold weight; delimiters revealed at selection |
| Italic | *italic* or _italic_ |
Italic style; delimiters revealed at selection |
| Strikethrough | ~~strikethrough~~ |
Strikethrough line; delimiters revealed at caret |
| Highlight | ==highlight== |
Highlight tint background; delimiters revealed |
| Inline Code | `code` |
Monospace font with subtle background pill |
| Fenced Code Blocks | ```dart ... ``` |
Monospace block with background fill |
| Blockquotes | > Quoted line |
Left border accent decoration with inset text |
| Unordered Lists | - Item or * Item or + Item |
Bullet markers with smart list continuation |
| Ordered Lists | 1. Item or 2. Item |
Decimal numerals with auto-increment on Enter |
| Task Lists | - [ ] Pending or - [x] Done |
Interactive checkbox widgets (toggle in-place) |
| Horizontal Rules | --- or *** or ___ |
Rendered divider line decoration |
| Links | [Label](https://...) |
Clickable/tappable link with URL tap callback |
Installation #
Add live_markdown_editor to your pubspec.yaml:
dependencies:
live_markdown_editor: ^0.5.3
Or run:
flutter pub add live_markdown_editor
Quick Start #
import 'package:flutter/widgets.dart';
import 'package:live_markdown_editor/live_markdown_editor.dart';
class MyEditorPage extends StatefulWidget {
const MyEditorPage({super.key});
@override
State<MyEditorPage> createState() => _MyEditorPageState();
}
class _MyEditorPageState extends State<MyEditorPage> {
late final MarkdownEditorController _controller = MarkdownEditorController(
text: '# My Notes\n\n- [ ] Ship to production\n- [x] Write tests',
);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LiveMarkdownEditor(
controller: _controller,
onChanged: (markdown) => print('Saved: $markdown'),
onLinkTap: (url) => print('Open: $url'),
);
}
}
Modes #
The canonical source of truth is always controller.text (UTF-16
TextEditingValue). Modes differ only in visual projection and delimiter
visibility:
| Mode | Property | Behavior |
|---|---|---|
| Raw | MarkdownEditorMode.raw |
Displays literal Markdown source code without hiding any delimiters. |
| Live | MarkdownEditorMode.live |
Formats text visually, renders task boxes as interactive widgets, and reveals delimiters only when the caret or selection touches them. |
| Read | MarkdownEditorMode.read |
Delimiter-free reading view. Preserves text selection, link taps, and in-place task checkbox toggling. |
Switch modes smoothly at runtime without losing selection, undo history, or scroll position:
_controller.mode = MarkdownEditorMode.read;
Customization & Advanced Usage #
1. Theme Configuration (MarkdownEditorThemeData) #
Customize all visual properties (colors, typography, heading scales, task sizing, blockquote margins) using MarkdownEditorThemeData:
LiveMarkdownEditor(
controller: _controller,
theme: MarkdownEditorThemeData.light().copyWith(
accentColor: const Color(0xFFC03A1E),
backgroundColor: const Color(0xFFFBF6E9),
textColor: const Color(0xFF221C10),
bodyStyle: const TextStyle(
fontSize: 17,
height: 1.7,
fontFamily: 'IBMPlexSans',
),
taskCheckedColor: const Color(0xFFC03A1E),
quoteBorderColor: const Color(0xFFC9BBA0),
quoteBorderWidth: 3.0,
contentPadding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
),
)
2. Replaceable Component Builders (MarkdownEditorComponentBuilders) #
Customize task checkboxes or selection handle presentation while maintaining package-owned gestures and geometry:
LiveMarkdownEditor(
controller: _controller,
componentBuilders: MarkdownEditorComponentBuilders(
// Inject a custom task toggle widget (e.g. Material Checkbox or CupertinoSwitch):
taskToggleBuilder: (context, taskContext) => Checkbox(
value: taskContext.checked,
onChanged: taskContext.enabled ? (_) => taskContext.onToggle() : null,
),
),
)
3. Editing Behavior (MarkdownEditingBehavior) #
Control typing auto-pairing, wrapping, and list continuations:
LiveMarkdownEditor(
controller: _controller,
behavior: const MarkdownEditingBehavior(
autoPair: true, // Auto-pairs delimiters like **, ``, _, ~~
wrapSelection: true, // Typing a delimiter around selected text wraps it
smartLists: true, // Enter on a list/task item auto-continues the list
autoCloseFences: true, // Triple backticks auto-closes the code fence block
),
)
4. Bidi & RTL Direction (directionMode) #
Supports per-line first-strong bidirectional text shaping:
LiveMarkdownEditor(
controller: _controller,
// MarkdownDirectionMode.aware resolves direction independently per hard line:
directionMode: MarkdownDirectionMode.aware,
fallbackDirection: TextDirection.ltr,
)
MarkdownDirectionMode.aware(default): Analyzes first-strong letters of each line independently (ideal for mixed Arabic/English documents).MarkdownDirectionMode.auto: Computes document-level direction.MarkdownDirectionMode.ltr/MarkdownDirectionMode.rtl: Forces a fixed text direction.
5. Programmatic Commands & Undo/Redo #
Execute atomic, undoable formatting transactions directly on the controller:
// Formatting commands
_controller.execute(MarkdownCommand.bold);
_controller.execute(MarkdownCommand.italic);
_controller.execute(MarkdownCommand.strikethrough);
_controller.execute(MarkdownCommand.code);
_controller.execute(MarkdownCommand.link, argument: 'https://flutter.cn');
_controller.execute(MarkdownCommand.taskList);
_controller.execute(MarkdownCommand.blockquote);
// History control
if (_controller.canUndo) _controller.undo();
if (_controller.canRedo) _controller.redo();
Example App #
Check out example/ for The Manuscript Casebook, a showcase featuring:
- Framework-neutral (Foundation), Material, and Cupertino host embeddings
- Arabic task lists, headlines, and blockquotes with RTL shaping
- Bilingual Latin/Arabic font pairing (Fraunces + Amiri + IBM Plex Sans)
- Responsive mobile, tablet, and desktop adaptive layouts
To run the demo:
cd example
flutter run
License #
MIT License — see LICENSE for details.