flutter_md
Markdown library written in Dart. It can parse and display Markdown.
0.2.0
4.3K downloads/mo
#4073 most downloaded on pub.dev
DoctorinaAI/md
What this package is like to depend on
Last release 18 days ago
05 Aug 2026
Ships unpredictably
gaps range from 8 days to 10 months
Nearly every release is documented
notes for 9 of 9 stable releases
Nothing withdrawn
no release was ever pulled
1 years old
9 releases · first in 2025
4 releases in the last 12 months
see the full history below
Release timeline
9 releases · Jun 2025 to Aug 2026Releases
latest 9-
0.2.005 Aug 2026Release notes
Open source →0.2.0
Upgrading from 0.0.x? See the
migration guide. 0.2.x is almost entirely
backward compatible — the only required code change is a newalertbranch
for directMD$Block.map/switchcallers.- ADDED: Opt-in, dependency-free syntax highlighting for fenced code blocks
(65+ languages, GitHub light/dark themes). Assign aSyntaxHighlighterto the
newMarkdownThemeData.highlighterfield; the default (unset) renders code as
plain monospace, so existing usage is unchanged. New public API on
package:flutter_md/highlight.dart:SyntaxHighlighter,MarkdownHighlighter,
CodeHighlightTheme,Grammar,GrammarToken,compileHighlightPattern
(SyntaxHighlighter/CodeHighlightThemeare also re-exported from the main
entrypoint). Each language is its own library
(package:flutter_md/highlight/<lang>.dart, e.g.HighlightDart.grammar) with
no central registry, so importing one never references the others and unused
grammars tree-shake away — a Dart-only app adds ~0 beyond the engine; all 65
add ~62 KB gzipped.HighlightThemes.githubDark/githubLight
(highlight/themes.dart) provide ready themes;allHighlightLanguages
(highlight/all.dart) is a convenience registry of every grammar for
demos/tooling (it references all languages, so unused ones can no longer
tree-shake away). The highlighter only partitions text — never edits it — so
selection and copy stay aligned. Grammars are generated by
tool/highlight_codegen(adapted from Prism, MIT). - ADDED: Cross-block and cross-widget text selection. A
MarkdownSelectionControlleranchors the selection on the immutable model, so
it spans multiple blocks and multipleMarkdownWidgets and survives list
disposal (e.g. chat scrolling). New public API:MarkdownSelectionController,
MarkdownSelectionScope,MarkdownSelectionGroup,MarkdownPosition,
MarkdownSelection,MarkdownDocumentRef,MarkdownSelectedContent
(+ document/block),MarkdownSelectionFormatter/
MarkdownPlainTextFormatter/MarkdownMarkupFormatter,
MarkdownReconciliationPolicy,MarkdownSelectionSurface,
markdownBlockRenderedText, and
SelectableBlockPainter/SelectableTextBlock. - ADDED:
StreamingMarkdownParser, an incremental parser for streaming
sources such as LLM token output. It freezes completed blocks (a block ends at
a blank line, outside any open code fence) so only the still-growing tail is
re-parsed as tokens arrive — turning theO(N²)cost of re-parsing the whole
buffer on every token into roughlyO(tail)(3–14× faster on a full message
stream inbenchmark/streaming_benchmark.dart).parser.add(chunk)returns
the growingMarkdown, always identical block-for-block to
Markdown.fromString(everythingSoFar), and aStream<String>.toMarkdown()
extension wires it into a stream transform. Pass a configuredMarkdownDecoder
(e.g.inlineMath: true) to matchMarkdown.fromString. The batch
MarkdownDecoderhot path is byte-for-byte unchanged. - ADDED:
MarkdownMarkupFormatter, a built-in "Copy as Markdown" formatter.
Pass it togetText()(or setcontroller.formatter) to reconstruct Markdown
structure on copy — heading#s, nested list markers with task checkboxes,
blockquote/alert>prefixes, fenced code and pipe tables — for blocks the
selection covers in full; partially-selected boundary blocks fall back to the
plain sliced text so nothing outside the selection is emitted. The default
copy behaviour is unchanged (MarkdownPlainTextFormatter). - ADDED:
MarkdownWidgetgains optionaldocumentIdandcontroller
parameters (resolved from the ambient scope). Backward compatible: a widget
with nodocumentIdis inert. - ADDED: Lists and tables are now interactively selectable. A new
MultiPainterSelectablemixin (+SelectableFragment) maps pointer positions
and highlight boxes across the manyTextPainters of a list's items or a
table's cells, so a drag can start or end inside a list item or table cell and
the copied text keeps the\n/\tseparators ofmarkdownBlockRenderedText. - ADDED: Keyboard shortcuts and a context toolbar on
MarkdownSelectionScope,
mirroringSelectableRegion/SelectableText. When focused:Ctrl/Cmd+C
copies,Ctrl/Cmd+Aselects all,Shift+arrows extend by character / word /
line / document (and vertically by geometry),Escclears. Right-click
(desktop) / long-press (mobile) shows an adaptive Copy / Select-all toolbar.
The scope is now aStatefulWidgetwith a publicMarkdownSelectionScopeState
(copySelection/selectAll/clearSelection/showToolbar/
hideToolbar/contextMenuButtonItems/contextMenuAnchors). New
customization params:focusNode,enabled,selectionColor,
contextMenuBuilder,magnifierConfiguration,selectionControls,
onSelectionChanged. New controller ops:selectionColor,
globalSelectionRects,moveSelectionEdgeToGlobal, and the
extendSelectionBy*family;MarkdownPosition.copyWith. - ADDED: Native selection handles and a magnifier on touch platforms,
driven by Flutter'sSelectionOverlay. Selection endpoints push
LeaderLayers from the render objects so the handles follow the content as it
scrolls (and across multipleMarkdownWidgets); dragging a handle adjusts the
selection and shows the platform magnifier. Handles/magnifier respect the
platform (selectionControls,magnifierConfiguration) and are absent on
desktop, matchingSelectableText. New surface geometry:
localSelectionRects,setSelectionHandleLayers,repaintSelection, and
MarkdownSelectionController.selectionHandleEndpoints/
MarkdownHandleEndpoints. - ADDED: Word- and block-granular selection gestures. Double-click/tap
selects the word under the pointer, triple-click/tap selects the whole block,
a single click collapses (clears) the selection, andShift-click extends it.
Dragging after a double/triple click keeps word/block granularity; a touch
long-press grabs the whole word (then extends by word), and a touch
double-tap selects the word and pops the toolbar. Word boundaries use the
platform word segmentation (TextPainter.getWordBoundary), so double-click
keeps intra-word punctuation like apostrophes (can't). New controller ops:
selectWordAtGlobal,selectBlockAtGlobal,wordSelectionAt,
blockSelectionAt,extendSelectionGranular, andwordRangeIn; new surface
geometryMarkdownSelectionSurface.wordBoundaryForGlobal. - ADDED: Mouse cursor feedback — a
MarkdownWidgetshows the click (hand)
cursor over actionable links, the text (I-beam) cursor while it participates
in a selection controller, and otherwise the default cursor. - CHANGED:
MarkdownWidget's render object now draws the selection
highlight outside the cached contentPictureand becomes a repaint boundary
when selectable, so selection/drag repaints do not rebuild the glyph cache.
The highlight color is now customizable via the controller / scope. The
highlight is painted on top of (rather than beneath) the glyphs, so a
translucent selection stays visible over opaque backgrounds — code fences,
inline code, and==marked==spans. - EXAMPLE: Reworked the demo tabs — a longer, richer chat (tables, code,
nested/task lists, alerts, math, token-by-token streaming with a typing
indicator, Select-all/Clear) and a Selection tab that spans every block type.
0.1.0
- ADDED: GitHub-style alert blocks (
> [!NOTE],> [!TIP],> [!IMPORTANT],
> [!WARNING],> [!CAUTION]) via the newMD$Alertblock andMD$AlertType. - ADDED: GitHub task-list items (
- [ ]/- [x]) viaMD$ListItem.checked
andMD$ListItem.isTask, rendered with a checkbox. - ADDED: Table column alignment (
:---,:--:,---:) captured on
MD$Table.alignmentsand applied when rendering. - ADDED:
linkStyleonMarkdownThemeDatato customize link text styling
(thanks @inamhusain, #22). - ADDED: Per-type alert accent colors via
MarkdownThemeData.alertColors
andalertColorFor. - ADDED: Opt-in
$...$inline LaTeX math conversion to Unicode, disabled
by default. Enable withMarkdownDecoder(inlineMath: true)or
Markdown.fromString(text, inlineMath: true). Supports LaTeX commands
(\alpha,\rightarrow, ...), superscripts/subscripts (x^2,H_2O,
x^{10}), is code-span and code-block safe, and preserves currency ($5).
The command table is configurable viamathReplacements(extend the
exportedkMarkdownMathCommands). Originally proposed in #21 by
@ibragimov05. - FIXED:
\$is now a recognized backslash escape, producing a literal
dollar sign (and opting a$...$run out of math conversion). - CHANGED: Thematic breaks now support
***and___(and spaced variants
like- - -), and no longer greedily consume text after---. - CHANGED:
~~~fenced code blocks are now recognized in addition to```. - FIXED: Emphasis no longer leaks to the end of the line for stray or
unterminated markers (e.g.5 * 6 = 30,**bold never closed). - FIXED: Intraword underscores are no longer treated as emphasis
(e.g.snake_case,object_idare preserved). - FIXED: ATX headings require a space after
#;#hashtagand 7+#
are no longer headings, and trailing#sequences are stripped. - FIXED: Emphasis surrounding a link/image is now merged onto the link span.
- FIXED: Link/image targets support
<url>and single-quoted titles. - FIXED:
MarkdownThemeData.copyWithno longer dropsbuilderandonLinkTap. - BREAKING:
MD$Block.map/maybeMapgained analertbranch for the new
MD$Alertblock type. - PERFORMANCE: Rewrote the parser hot path — a single-span fast path for
plain text, first-code-unit guards that keep regexes off paragraph lines,
hand-rolled list-line and link-target parsing (removing per-line / per-link
RegExpallocation), lazy link-extraction gated on[, and a range-copy
escape rebuild (no more per-character hash-set lookups). Together with math
now being opt-in, the default parse path is roughly 45% faster across
representative workloads (links −68%, lists −61%, escapes −68%). Output is
byte-identical, guarded by a golden snapshot test. - TESTS: Added a golden characterization snapshot, a corner-case regression
suite, span-offset invariants, and unit tests for the node model, theme, and
widget; wired every test file intotest/unit_test.dartso CI runs the full
suite (370+ tests, previously only a fraction ran).parser.dart,
nodes.dart,markdown.dart,theme.dart, andwidget.dartare now at
~100% line coverage. - ADDED:
benchmark/parser_benchmark.dart(a multi-scenario
benchmark_harnesssuite) andbenchmark/compare.dart(a low-noise
before/after comparison tool). - DOCS: Documented alerts, task lists, table alignment, thematic-break
variants, and opt-in inline math in the README.
Release notes
Open source →Upgrading from 0.0.x? See the migration guide. 0.2.x is almost entirely backward compatible — the only required code change is a new
alertbranch for directMD$Block.map/switchcallers.- ADDED: Opt-in, dependency-free syntax highlighting for fenced code blocks
(65+ languages, GitHub light/dark themes). Assign a
SyntaxHighlighterto the newMarkdownThemeData.highlighterfield; the default (unset) renders code as plain monospace, so existing usage is unchanged. New public API onpackage:flutter_md/highlight.dart:SyntaxHighlighter,MarkdownHighlighter,CodeHighlightTheme,Grammar,GrammarToken,compileHighlightPattern(SyntaxHighlighter/CodeHighlightThemeare also re-exported from the main entrypoint). Each language is its own library (package:flutter_md/highlight/<lang>.dart, e.g.HighlightDart.grammar) with no central registry, so importing one never references the others and unused grammars tree-shake away — a Dart-only app adds ~0 beyond the engine; all 65 add ~62 KB gzipped.HighlightThemes.githubDark/githubLight(highlight/themes.dart) provide ready themes;allHighlightLanguages(highlight/all.dart) is a convenience registry of every grammar for demos/tooling (it references all languages, so unused ones can no longer tree-shake away). The highlighter only partitions text — never edits it — so selection and copy stay aligned. Grammars are generated bytool/highlight_codegen(adapted from Prism, MIT). - ADDED: Cross-block and cross-widget text selection. A
MarkdownSelectionControlleranchors the selection on the immutable model, so it spans multiple blocks and multipleMarkdownWidgets and survives list disposal (e.g. chat scrolling). New public API:MarkdownSelectionController,MarkdownSelectionScope,MarkdownSelectionGroup,MarkdownPosition,MarkdownSelection,MarkdownDocumentRef,MarkdownSelectedContent(+ document/block),MarkdownSelectionFormatter/MarkdownPlainTextFormatter/MarkdownMarkupFormatter,MarkdownReconciliationPolicy,MarkdownSelectionSurface,markdownBlockRenderedText, andSelectableBlockPainter/SelectableTextBlock. - ADDED:
StreamingMarkdownParser, an incremental parser for streaming sources such as LLM token output. It freezes completed blocks (a block ends at a blank line, outside any open code fence) so only the still-growing tail is re-parsed as tokens arrive — turning theO(N²)cost of re-parsing the whole buffer on every token into roughlyO(tail)(3–14× faster on a full message stream inbenchmark/streaming_benchmark.dart).parser.add(chunk)returns the growingMarkdown, always identical block-for-block toMarkdown.fromString(everythingSoFar), and aStream<String>.toMarkdown()extension wires it into a stream transform. Pass a configuredMarkdownDecoder(e.g.inlineMath: true) to matchMarkdown.fromString. The batchMarkdownDecoderhot path is byte-for-byte unchanged. - ADDED:
MarkdownMarkupFormatter, a built-in "Copy as Markdown" formatter. Pass it togetText()(or setcontroller.formatter) to reconstruct Markdown structure on copy — heading#s, nested list markers with task checkboxes, blockquote/alert>prefixes, fenced code and pipe tables — for blocks the selection covers in full; partially-selected boundary blocks fall back to the plain sliced text so nothing outside the selection is emitted. The default copy behaviour is unchanged (MarkdownPlainTextFormatter). - ADDED:
MarkdownWidgetgains optionaldocumentIdandcontrollerparameters (resolved from the ambient scope). Backward compatible: a widget with nodocumentIdis inert. - ADDED: Lists and tables are now interactively selectable. A new
MultiPainterSelectablemixin (+SelectableFragment) maps pointer positions and highlight boxes across the manyTextPainters of a list's items or a table's cells, so a drag can start or end inside a list item or table cell and the copied text keeps the\n/\tseparators ofmarkdownBlockRenderedText. - ADDED: Keyboard shortcuts and a context toolbar on
MarkdownSelectionScope, mirroringSelectableRegion/SelectableText. When focused:Ctrl/Cmd+Ccopies,Ctrl/Cmd+Aselects all,Shift+arrows extend by character / word / line / document (and vertically by geometry),Escclears. Right-click (desktop) / long-press (mobile) shows an adaptive Copy / Select-all toolbar. The scope is now aStatefulWidgetwith a publicMarkdownSelectionScopeState(copySelection/selectAll/clearSelection/showToolbar/hideToolbar/contextMenuButtonItems/contextMenuAnchors). New customization params:focusNode,enabled,selectionColor,contextMenuBuilder,magnifierConfiguration,selectionControls,onSelectionChanged. New controller ops:selectionColor,globalSelectionRects,moveSelectionEdgeToGlobal, and theextendSelectionBy*family;MarkdownPosition.copyWith. - ADDED: Native selection handles and a magnifier on touch platforms,
driven by Flutter's
SelectionOverlay. Selection endpoints pushLeaderLayers from the render objects so the handles follow the content as it scrolls (and across multipleMarkdownWidgets); dragging a handle adjusts the selection and shows the platform magnifier. Handles/magnifier respect the platform (selectionControls,magnifierConfiguration) and are absent on desktop, matchingSelectableText. New surface geometry:localSelectionRects,setSelectionHandleLayers,repaintSelection, andMarkdownSelectionController.selectionHandleEndpoints/MarkdownHandleEndpoints. - ADDED: Word- and block-granular selection gestures. Double-click/tap
selects the word under the pointer, triple-click/tap selects the whole block,
a single click collapses (clears) the selection, and
Shift-click extends it. Dragging after a double/triple click keeps word/block granularity; a touch long-press grabs the whole word (then extends by word), and a touch double-tap selects the word and pops the toolbar. Word boundaries use the platform word segmentation (TextPainter.getWordBoundary), so double-click keeps intra-word punctuation like apostrophes (can't). New controller ops:selectWordAtGlobal,selectBlockAtGlobal,wordSelectionAt,blockSelectionAt,extendSelectionGranular, andwordRangeIn; new surface geometryMarkdownSelectionSurface.wordBoundaryForGlobal. - ADDED: Mouse cursor feedback — a
MarkdownWidgetshows the click (hand) cursor over actionable links, the text (I-beam) cursor while it participates in a selection controller, and otherwise the default cursor. - CHANGED:
MarkdownWidget's render object now draws the selection highlight outside the cached contentPictureand becomes a repaint boundary when selectable, so selection/drag repaints do not rebuild the glyph cache. The highlight color is now customizable via the controller / scope. The highlight is painted on top of (rather than beneath) the glyphs, so a translucent selection stays visible over opaque backgrounds — code fences,inline code, and==marked==spans. - EXAMPLE: Reworked the demo tabs — a longer, richer chat (tables, code, nested/task lists, alerts, math, token-by-token streaming with a typing indicator, Select-all/Clear) and a Selection tab that spans every block type.
- ADDED: Opt-in, dependency-free syntax highlighting for fenced code blocks
-
0.0.809 Oct 2025Release notes
Open source →What's Changed
- Invalidate and relayout render object after system fonts changed by @mike-doctorina in #17
- New table render by @dened in #15
Full Changelog: 0.0.7...0.0.8
Release notes
Open source →- CHANGED: New table render
- FIXED: Invalidate and relayout render object after system fonts changed.
-
0.0.710 Sep 2025Release notes
Open source →V 0.0.7 (#13)
* feat: support theming
* feat: add divider color to MarkdownThemeData and update BlockPainter$Quote
* fix: Inline code incorrectly parses inner markdown syntax (#10)
* fix: No indentation after line breaks within list item (#4)
* chore: update version to 0.0.7 and enhance changelog with recent fixes and features
Release notes
Open source →- FIXED: Preserved indentation on line breaks within list items [#4].
- FIXED: Inline code no longer processes inner Markdown syntax [#10].
- CHANGED: Improved theme support.
- ADDED: Dark mode support in the example app.
-
0.0.602 Sep 2025 -
0.0.516 Jul 2025Release notes
Open source →- FIXED: Fixed parsing url such as
[text](https://domain.com/path(with)brackets).
Release notes
Open source →- FIXED: Fixed parsing url such as
[text](https://domain.com/path(with)brackets).
- FIXED: Fixed parsing url such as
-
0.0.408 Jul 2025 -
0.0.320 Jun 2025 -
0.0.219 Jun 2025Release notes
Open source →ADDED: All field in MarkdownThemeData() are now optional.
ADDED: MarkdownThemeData{}.headingStyleFor method to customize heading styles.
FIXED: Remove clipping for canvas. Fixes one line text trim at browsers.
FIXED: Correctly apply styles to text in blocks.Release notes
Open source →- ADDED: All field in
MarkdownThemeData()are now optional. - ADDED:
MarkdownThemeData{}.headingStyleFormethod to customize heading styles. - FIXED: Remove clipping for canvas. Fixes one line text trim at browsers.
- FIXED: Correctly apply styles to text in blocks.
- ADDED: All field in
-
0.0.118 Jun 2025