flutter_smart_editor 2.3.0
flutter_smart_editor: ^2.3.0 copied to clipboard
A pure Flutter rich text HTML editor with no WebView dependency. Fully customizable with granular settings for editor, toolbar, scroll, keyboard, selection, and styling.
Flutter Smart Editor #
A highly customizable, pure Dart and Flutter rich text HTML editor. No WebViews, no JavaScriptβbuilt entirely for native performance and full control.
flutter_smart_editor is a full-featured WYSIWYG editor designed from scratch to eliminate the overhead and bugs associated with WebView-based editors. It provides a premium, Material 3 experience with clean HTML input/output.
βοΈ Enjoying Flutter Smart Editor? Please consider giving it a like on pub.dev and starring the repository on GitHub!
π€ Want to contribute? We welcome all collaborations, bug reports, and feature requests. Feel free to open an issue or submit a pull request on GitHub.
πΈ Showcase #
| Light Mode Editor | Dark Mode & Rich Formatting |
|---|---|
![]() |
![]() |
| Material 3 Toolbars | Multi-Media Attachments |
![]() |
![]() |
| Floating Find & Replace | Interactive Hyperlinks |
![]() |
![]() |
| Rich Text Styling (Light) | Pure Dart Architecture |
![]() |
![]() |
π Table of Contents #
- π‘ Why Smart Editor?
- β¨ Features
- π Getting Started
- π Basic Usage
- ποΈ Toolbar Customization
- π Migration Guide
- βοΈ Detailed Configuration
- π οΈ Upcoming Features
- β Troubleshooting
- π License
π‘ Why Smart Editor? #
Most rich text editors for Flutter rely heavily on hidden WebViews and JavaScript bridges (flutter_inappwebview, webview_flutter, or embedded contenteditable HTML elements). While fast to prototype, WebView-based solutions face significant architectural limitations:
- Heavy overhead & jank: Running a complete browser engine under the hood introduces unnecessary memory consumption and micro-stutters.
- Keyboard & focus glitches: Fragile IME integration, virtual keyboard resizing issues, and unreliable cursor positioning across iOS and Android.
- Platform view constraints: Inability to seamlessly embed native Flutter widgets (such as custom audio players, video renderers, or interactive buttons) into the text flow.
Flutter Smart Editor is engineered 100% in pure Dart and Flutter:
- β‘ Zero WebViews & Zero JavaScript: Rendered and laid out completely through Flutter's native widget and render object tree for 60/120 FPS fluidity.
- π οΈ Complete Freedom & Total Customization: Gives you a complete free hand to use it exactly as you want. You can fully customize anythingβfrom toolbar layouts, button groups, and color schemes to custom block builders (audio, video, document attachments), dialogs, and event callbacks.
- π± Native Performance & Fluidity: Natural touch gestures, seamless platform scrolling, and rock-solid platform IME text editing.
- π Clean HTML In & Out: Robust internal HTML parser and serializer producing clean, semantic HTML without proprietary bloat.
β¨ Features #
π¨ Formatting & Styling #
- Inline Styles: Bold, Italic, Underline, and Strikethrough.
- Dynamic Fonts: Custom Font Family and Font Size selection.
- Rich Colors: Foreground (text) and Highlight (background) color pickers.
- Block Types: Paragraphs and Headings (H1βH6).
- Alignment: Left, Center, Right, and Justify.
π§© Core Editor Capabilities #
- Pure HTML: Clean output and robust parsing of existing HTML content.
- Dynamic Height: The editor expands as you type and can be limited via
maxLines. - Native Paste: Premium clipboard supportβpaste rich text/HTML from browsers and other apps.
- Lists (v2.1+): Robust, atomic Bullet and Numbered lists with smart reordering.
- Tables (v2.1+): Full support for HTML tables with dynamic row/column management (insertion, deletion, and cell updates).
- Hyperlinks (v2.2+): Native link insertion, editing, and removal dialogs, URL normalization, display text replacement, and custom tap listeners.
- Find & Replace (v2.2+): Floating search overlay with real-time match highlighting, regex, whole word, match case, hyperlink preservation, and atomic single-step undo.
- Media & Attachments (v2.3+): Comprehensive support for Images, Videos, Audio, and Documents with unified attachment dialogs, device picker delegates, interactive image cropping, custom media player builders (
audioBuilder,videoBuilder), captions, alt text, and full HTML serialization. - Mobile Optimized: Smart backspace bridge for soft keyboards and accessory bar avoidance.
- Undo/Redo: Built-in history management.
- Material 3 Toolbar: Scrollable, Grid, or enhanced Expandable layouts with clean single-toolbar toggling and dynamic wrap spacing.
π Getting Started #
Add flutter_smart_editor to your pubspec.yaml:
dependencies:
flutter_smart_editor: ^2.3.0
π Basic Usage #
import 'package:flutter_smart_editor/flutter_smart_editor.dart';
// ... inside your widget ...
SmartEditor(
controller: _controller,
editorSettings: const SmartEditorSettings(
hint: 'Start typing...',
initialText: '<p>Hello <b>World</b></p>',
),
toolbarSettings: const SmartToolbarSettings(
toolbarType: SmartToolbarType.scrollable,
),
)
ποΈ Toolbar Customization #
The toolbar is built using modular button groups. You can fully customize which groups appear and which specific buttons within those groups are active.
Full Toolbar Example #
Here is how you would configure a toolbar with every available group active:
SmartToolbarSettings(
toolbarType: SmartToolbarType.expandable,
defaultButtons: [
const SmartStyleButtons(), // Heading 1-6 & Paragraph
const SmartFontButtons(
strikethrough: true,
fontSize: true,
clearAll: true,
),
const SmartColorButtons(
foregroundColor: true,
highlightColor: true,
),
const SmartListButtons(
ul: true,
ol: true,
hr: true,
listStyles: true, // Enables interactive bullet picker
),
const SmartFontFamilyButtons(),
const SmartParagraphButtons(), // Alignment: Left, Center, Right, Justify
const SmartOtherButtons(
undo: true,
redo: true,
copy: true,
paste: true,
),
],
)
Breakdown of Button Groups #
| Group Class | Description |
|---|---|
SmartStyleButtons |
Controls the paragraph style dropdown (Heading 1 to Heading 6 and Normal text). |
SmartFontButtons |
Standard formatting: Bold, Italic, Underline, Strikethrough, Font Size, and Clear Formatting. |
SmartColorButtons |
Integrated color pickers for text color and background highlight color. |
SmartFontFamilyButtons |
A dropdown for selecting from your application's available font families. |
SmartListButtons |
Bullet/Numbered list toggles, horizontal dividers (HR), and the premium Bullet Style Picker. |
SmartParagraphButtons |
Text alignment controls: Left, Center, Right, and Full Justify. |
SmartInsertButtons |
Insert elements: Hyperlinks (link: true), Multi-Media Attachments (attachment: true for images, video, audio, documents), and HTML Tables (table: true). |
SmartOtherButtons |
Utility actions: Undo, Redo, Copy to Clipboard, Paste, and Find & Replace (findReplace: true). |
2. SmartToolbarSettings #
Layout & Position
| Parameter | Type | Default | Description |
|---|---|---|---|
toolbarType |
SmartToolbarType |
.scrollable |
Layout: .scrollable, .grid, or enhanced .expandable (single toolbar with smooth toggle between scroll and multi-line wrap). |
toolbarPosition |
SmartToolbarPosition |
.above |
Position relative to editor: .above or .below. |
initiallyExpanded |
bool |
false |
Starts the expandable toolbar in the open state. |
showBorder |
bool |
false |
Separation border between editor and toolbar. |
showSeparators |
bool |
true |
Vertical lines between button groups. |
itemHeight |
double |
36 |
Height of individual buttons and chips. |
gridSpacingH |
double |
5 |
Horizontal gap between buttons in grid layout. |
gridSpacingV |
double |
5 |
Vertical gap between buttons in grid layout. |
separatorWidget |
Widget? |
null |
Custom widget to use as a separator. |
Content
| Parameter | Type | Default | Description |
|---|---|---|---|
defaultButtons |
List<SmartToolbarGroup> |
[...] |
List of button groups to display. |
customButtons |
List<Widget> |
[] |
Custom widgets to insert into the toolbar. |
customButtonInsertionIndices |
List<int> |
[] |
Position indices for custom buttons. |
Container Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
BoxDecoration? |
null |
Styling for the toolbar background/border. |
padding |
EdgeInsets? |
null |
Internal padding of the toolbar container. |
Button & Text Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
buttonColor |
Color? |
null |
Base color for toolbar icons. |
buttonSelectedColor |
Color? |
null |
Icon color when a style is active. |
buttonFillColor |
Color? |
null |
Background color of the button. |
buttonBorderRadius |
BorderRadius? |
null |
Corner rounding for buttons. |
buttonIconSize |
double |
20.0 |
Size of the toolbar icons. |
textStyle |
TextStyle? |
null |
Style for text labels in the toolbar. |
Dropdown Styling
| Parameter | Type | Default | Description |
|---|---|---|---|
dropdownBackgroundColor |
Color? |
null |
Background color of popup menus. |
dropdownElevation |
int |
8 |
Shadow depth for popup menus. |
dropdownItemHeight |
double? |
null |
Height of items inside dropdowns. |
dropdownIconSize |
double |
24 |
Size of the dropdown arrow icon. |
Interceptors
| Callback | Signature | Description |
|---|---|---|
onButtonPressed |
(Type, bool, Fn) |
Intercept any button click to add custom logic. |
onDropdownChanged |
(Type, val, Fn) |
Intercept any dropdown selection change. |
π Migration Guide (v1.0.x β v2.x.x) #
Version 2.x.x introduces a Unified Settings API. Instead of separate ScrollSettings, KeyboardSettings, etc., all editor-related properties are now in SmartEditorSettings.
Old:
SmartEditor(
scrollSettings: SmartScrollSettings(autoAdjustHeight: true),
styleSettings: SmartStyleSettings(editorPadding: EdgeInsets.all(16)),
)
New:
SmartEditor(
editorSettings: SmartEditorSettings(
autoAdjustHeight: true,
editorPadding: EdgeInsets.all(16),
),
)
βοΈ Detailed Configuration #
1. SmartEditorSettings #
Core & HTML
| Parameter | Type | Default | Description |
|---|---|---|---|
initialText |
String? |
null |
The starting HTML content in the editor. |
hint |
String? |
null |
Placeholder text shown when the editor is empty. |
defaultFontSize |
double |
16.0 |
The base font size for paragraph text. |
darkMode |
bool? |
null |
Force light or dark mode. If null, follows system brightness. |
disabled |
bool |
false |
Completely disables interaction and grays out the editor. |
readOnly |
bool |
false |
Disables text input but allows selection and copying. |
maxLines |
int? |
null |
Max height in lines before scrolling. null = grows indefinitely. |
characterLimit |
int? |
null |
Max number of characters allowed in the editor. |
spellCheck |
bool |
false |
Enables browser/OS native spell checking. |
processInputHtml |
bool |
true |
Sanitizes and prepares input HTML string. |
processOutputHtml |
bool |
true |
Cleans up empty tags in the produced HTML output. |
processNewLineAsBr |
bool |
false |
Converts \n to <br> in input strings. |
Scroll & Layout
| Parameter | Type | Default | Description |
|---|---|---|---|
autoAdjustHeight |
bool |
true |
Allows the editor to grow vertically as the user types. |
ensureVisible |
bool |
false |
Scrolls the editor into view when it gains focus. |
scrollPhysics |
ScrollPhysics? |
null |
Custom physics for the editor's scroll view. |
Keyboard
| Parameter | Type | Default | Description |
|---|---|---|---|
inputType |
SmartInputType |
.text |
The type of virtual keyboard to display. |
autofocus |
bool |
false |
Opens the keyboard immediately on mount. |
textInputAction |
TextInputAction? |
null |
The action button on the keyboard (e.g. Done, Search). |
keyboardAppearance |
Brightness? |
null |
Force a dark or light keyboard on iOS. |
adjustForKeyboard |
bool |
true |
Automatically shrinks the editor when the keyboard appears. |
Selection & Cursor
| Parameter | Type | Default | Description |
|---|---|---|---|
cursorColor |
Color? |
Theme |
The color of the blinking vertical text cursor. |
cursorWidth |
double |
2.0 |
Width of the cursor in logical pixels. |
cursorRadius |
Radius? |
Circular(2) |
Corner rounding of the cursor tip. |
cursorHeight |
double? |
null |
Fixed height for the cursor. |
showCursor |
bool |
true |
Whether to show the blinking cursor at all. |
selectionColor |
Color? |
Theme |
Background color for highlighted text. |
selectionHandleColor |
Color? |
Theme |
Color of the drag handles on mobile. |
enableInteractiveSelection |
bool |
true |
Allows users to select text via tap/hold. |
Style & Decoration
| Parameter | Type | Default | Description |
|---|---|---|---|
decoration |
BoxDecoration? |
null |
Decoration around the entire editor container. |
editorDecoration |
BoxDecoration? |
null |
Decoration around just the text input area. |
editorPadding |
EdgeInsets |
all(12) |
Internal padding of the text input area. |
editorBackgroundColor |
Color? |
null |
Background color of the editing area. |
borderRadius |
BorderRadius? |
null |
Rounded corners for the default editor border. |
searchMatchColor |
Color? |
null |
Custom background highlight color for inactive search matches. |
searchActiveMatchColor |
Color? |
null |
Custom background highlight color for the active search match. |
Lists & Horizontal Rules
| Parameter | Type | Default | Description |
|---|---|---|---|
maxListDepth |
int |
3 |
Maximum nesting depth for lists (1-5 recommended). |
defaultBulletStyle |
SmartBulletStyle |
.filledCircle |
Default bullet shape for unordered lists. |
hrStyle |
SmartHrStyle |
(defaults) |
Visual configuration for Horizontal Rule dividers. |
draggableBlockTypes |
Set<BlockType> |
null |
Types of blocks that show reordering handles (e.g. {BlockType.bulletList}). |
Callbacks
| Callback | Signature | Description |
|---|---|---|
onChangeContent |
(String? html) |
Triggered whenever text or formatting changes. |
onFocus |
() |
Triggered when the editor gains focus. |
onBlur |
() |
Triggered when the editor loses focus. |
onInit |
() |
Triggered when the editor is fully initialized. |
onEnter |
() |
Triggered when the Enter/Return key is pressed. |
onChangeSelection |
(Map<String, dynamic>) |
Triggered when cursor moves; provides active formatting state. |
onPaste |
() |
Triggered when content is pasted into the editor. |
onTagSerialize |
(Type, Tag, Attr, Styles, Content) |
Custom tag serialization interceptor (see below). |
onLinkTapped |
(String url) |
Triggered when a hyperlink is tapped in the editor. |
onKeyUp / onKeyDown |
(String? key) |
Raw key event callbacks. |
Media & Attachment Builders (v2.3+)
| Parameter | Type | Default | Description |
|---|---|---|---|
audioBuilder |
AudioNodeBuilder? |
null |
Custom builder to render audio nodes using your preferred audio player. |
videoBuilder |
VideoNodeBuilder? |
null |
Custom builder to render video nodes using your preferred video player. |
π οΈ Interactive List Customization #
Version 2.1.0 introduces the ability for users to live-change their bullet point symbols (pointers).
Enabling the Bullet Picker
To show the bullet style picker in your toolbar, ensure listStyles is enabled in your SmartListButtons group:
SmartToolbarSettings(
defaultButtons: [
const SmartListButtons(
ul: true,
ol: true,
listStyles: true, // Enables the style picker button
),
],
)
Customizing Available Styles
You can filter which pointers are available to the user:
SmartListButtons(
listStyles: true,
availableStyles: [
SmartBulletStyle.filledCircle,
SmartBulletStyle.diamond,
SmartBulletStyle.star,
],
)
Custom Serialization Example
You can intercept any HTML tag before it is written to the output. The styles map allows you to precisely modify inline CSS properties without string parsing.
SmartEditorSettings(
onTagSerialize: (type, tag, attributes, styles, content) {
if (type == SmartTagType.bold) {
// Add custom inline style to bold tags
styles['color'] = 'royal-blue';
return null; // Return null to let the editor build the final HTML using modified styles
}
if (type == SmartTagType.heading1) {
// Completely replace h1 with a styled div
return '<div class="h1-alternate" style="text-shadow: 1px 1px #eee;">$content</div>';
}
return null;
},
)
4. Programmatic APIs (Tables, Lists, Hyperlinks, Search & Media Attachments) #
SmartEditorController provides a rich set of programmatic APIs to manipulate tables, lists, hyperlinks, and document search dynamically from your parent widgets, custom toolbar buttons, or keyboard listeners.
π Table Management APIs
When the user is interacting with tables, you can use these controller methods to perform programmatic modifications:
| Method / Getter | Return Type | Description |
|---|---|---|
insertTable({int rows, int cols}) |
void |
Inserts a new responsive HTML table grid with specified dimensions after the active block. |
insertRow() |
void |
Inserts a new table row below the currently focused table cell. |
insertColumn() |
void |
Inserts a new table column to the right of the currently focused table cell. |
deleteRow() |
void |
Deletes the row containing the currently focused table cell. |
deleteColumn() |
void |
Deletes the column containing the currently focused table cell. |
deleteTable() |
void |
Deletes the entire focused table block. |
isInsideTable |
bool |
Returns true if the caret/cursor is currently inside a table cell. |
focusedTableInfo |
({int blockIndex, int row, int col})? |
Returns the exact coordinate position of the focused cell, or null. |
Code Example: Context-Aware Table Modification
final controller = SmartEditorController();
// 1. Insert a 3x3 table programmatically
controller.insertTable(rows: 3, cols: 3);
// 2. perform context-aware row addition
if (controller.isInsideTable) {
print("Focused cell coordinates: ${controller.focusedTableInfo}");
// Add a new row below the focused cell
controller.insertRow();
}
π’ List & Numbered List APIs
To toggle lists and adjust indentation programmatically:
| Method / Getter | Arguments | Description |
|---|---|---|
setBlockType(BlockType type) |
BlockType.bulletList |
Converts the active block to an Unordered Bullet List (<ul>). |
setBlockType(BlockType type) |
BlockType.orderedList |
Converts the active block to an Ordered Numbered List (<ol>). |
setBlockType(BlockType type) |
BlockType.paragraph |
Converts a list item back to standard paragraph text (<p>). |
documentController.increaseIndent(int blockIndex) |
blockIndex |
Increases list nesting depth/indentation (supports up to 3 levels). |
documentController.decreaseIndent(int blockIndex) |
blockIndex |
Decreases list nesting depth/outdents the list block. |
Code Example: Programmatic List Customization
final controller = SmartEditorController();
// Convert current block to a Bullet List
controller.setBlockType(BlockType.bulletList);
// Convert current block to a Numbered List
controller.setBlockType(BlockType.orderedList);
// Increase Indentation on the active block index
final activeIndex = controller.documentController.focusedBlockIndex;
controller.documentController.increaseIndent(activeIndex);
π Hyperlink Management APIs
flutter_smart_editor provides native, full-lifecycle hyperlink management across both regular paragraphs and table cells. It supports interactive dialogs, automatic protocol normalization (http:// prefixing), display text replacements, link inspection, and removal.
| Method / Getter | Return Type | Description |
|---|---|---|
insertLink(String url, String displayText, {int? blockIndex, TextSelection? selection}) |
void |
Inserts a new link span at the cursor, applies a link to the selected text range, or updates the display text and URL atomically. |
removeLink({int? blockIndex, TextSelection? selection}) |
void |
Strips the hyperlink from the active selection or focused link span while preserving the text. |
getLinkInfo({int? blockIndex, TextSelection? selection}) |
Map<String, String?> |
Returns {'text': ..., 'url': ...} for the focused caret position or selected text. Works seamlessly inside both paragraphs and table cells. |
documentController.applyLink(int blockIndex, int start, int end, String? url) |
void |
Low-level document method applying or clearing a URL over a character range. |
documentController.setLink({...}) |
int |
Atomically applies or replaces a link in a block, appends a trailing unlinked space if needed, and returns the target cursor offset. |
documentController.setCellLink({...}) |
int |
Cell-level equivalent of setLink for table cells. |
editorSettings.onLinkTapped |
void Function(String url)? |
Callback invoked when a user clicks or taps a hyperlink in the editor. |
Code Example: Programmatic Link Insertion & Tap Handling
final controller = SmartEditorController();
// 1. Insert a link at the current cursor position
controller.insertLink(
'https://flutter.dev',
'Flutter Official Website',
);
// 2. Query existing link information at the cursor
final linkInfo = controller.getLinkInfo();
print('Active link: ${linkInfo['text']} -> ${linkInfo['url']}');
// 3. Remove a hyperlink from the selected text
controller.removeLink();
// 4. Handle link taps in the editor widget
SmartEditor(
controller: controller,
editorSettings: SmartEditorSettings(
onLinkTapped: (url) {
print('User clicked link: $url');
// Launch URL with url_launcher, open in-app webview, etc.
},
),
);
π Find & Replace APIs
Version 2.2.0 introduces a native floating Find & Replace search overlay anchored right over the editor. It supports live match counts, cycling navigation, regex queries, case sensitivity, whole-word matching, hyperlink preservation, and atomic single-step undo.
| Method / Getter | Return Type | Description |
|---|---|---|
showFindReplace({bool showReplace = false}) |
void |
Displays the floating search bar. Pass showReplace: true to open with the Replace row expanded. |
hideFindReplace() |
void |
Closes the search overlay, clears match highlights, and restores editor focus. |
setSearchQuery(String query) |
void |
Sets the search query and searches the document live across all blocks and table cells. |
setReplaceText(String text) |
void |
Sets the replacement string used by replaceCurrent and replaceAll. |
setSearchOptions(SearchOptions options) |
void |
Updates options (matchCase, wholeWord, isRegex, preserveLinkOnReplace) and recalculates matches. |
setReplaceExpanded(bool expanded) |
void |
Expands or collapses the Replace input row in the floating panel. |
findNext() |
void |
Cycles and smoothly navigates to the next match in the document. |
findPrevious() |
void |
Cycles and smoothly navigates to the previous match in the document. |
replaceCurrent() |
void |
Replaces the currently focused match with the replacement text. |
replaceAll() |
int |
Replaces all occurrences across all blocks and table cells in a single atomic undo step. Returns total count replaced. |
searchState |
SearchState |
Current search state snapshot (query, replaceText, matches, currentMatchIndex, isBarVisible, totalMatches, etc.). |
searchStateNotifier |
ValueNotifier<SearchState> |
Reactive notifier for listening to search overlay and match state changes. |
Search Options Configuration (SearchOptions)
| Option | Type | Default | Description |
|---|---|---|---|
matchCase |
bool |
false |
When true, matches are strictly case-sensitive. |
wholeWord |
bool |
false |
When true, matches only complete standalone words (\bquery\b). |
isRegex |
bool |
false |
When true, parses the query string as a standard Regular Expression. |
preserveLinkOnReplace |
bool |
true |
When true, replacing text inside a hyperlink updates the display text while keeping the hyperlink URL intact. |
Code Example: Programmatic Search & Batch Replacement
final controller = SmartEditorController();
// 1. Open the search overlay programmatically
controller.showFindReplace(showReplace: true);
// 2. Perform a live regex search
controller.setSearchOptions(const SearchOptions(
matchCase: true,
isRegex: true,
));
controller.setSearchQuery(r'\bFlutter\b');
// 3. Navigate through matches
controller.findNext();
print("Found ${controller.searchState.totalMatches} matches");
print("Current match index: ${controller.searchState.currentMatchIndex}");
// 4. Batch replace occurrences in a single undo step
controller.setReplaceText('Dart & Flutter');
final replacedCount = controller.replaceAll();
print("Replaced $replacedCount occurrences atomically!");
// 5. Customize search highlight colors in editor settings
SmartEditor(
controller: controller,
editorSettings: const SmartEditorSettings(
searchMatchColor: Color(0x66FFEB3B), // Highlight for all matches
searchActiveMatchColor: Color(0xCCFF9800), // Highlight for focused match
),
);
π Multi-Media & Attachment APIs (Images, Audio, Video & Documents)
Version 2.3.0 introduces a unified multi-media and attachment system supporting Images, Videos, Audio, and Documents with zero WebView dependencies. It includes a unified attachment dialog (SmartAttachmentDialog), bottom sheet selector (SmartAttachmentBottomSheet), camera/gallery picking, native image cropping, customizable audio/video player builders, captions, and full HTML round-trip serialization.
Node Models
| Model | Block Type | HTML Tag | Description |
|---|---|---|---|
ImageNode |
BlockType.image |
<img src="..." alt="..." data-caption="..." /> |
Image block with alignment, resizing, cropping, captions, and alt text. |
VideoNode |
BlockType.video |
<video src="..." poster="..."></video> |
Video block with optional captions, posters, and custom player builder support. |
AudioNode |
BlockType.audio |
<audio src="..." title="..."></audio> |
Audio block with titles, authors, and custom player builder support. |
DocumentNode |
BlockType.document |
<a href="..." data-attachment="document">...</a> |
Document attachment block showing filename, extension, and file size. |
Controller APIs (SmartEditorController)
| Method / Getter | Return Type | Description |
|---|---|---|
insertImage({required String src, String? alt, double? width, double? height, String? caption, SmartTextAlign alignment, int? blockIndex}) |
int |
Inserts an image block at or after blockIndex. Automatically appends an empty paragraph if at document end. |
updateImage(int blockIndex, {...}) |
void |
Updates properties (src, caption, dimensions, alignment) on an existing image block. |
setImageAlignment(int blockIndex, SmartTextAlign alignment) |
void |
Convenience method to change image alignment. |
setImageSize(int blockIndex, {double? width, double? height}) |
void |
Convenience method to resize an image block. |
removeImage(int blockIndex) |
void |
Deletes the image block at blockIndex. |
getImageNode(int blockIndex) |
ImageNode? |
Returns the ImageNode or null. |
insertVideo({required String src, String? caption, double? width, double? height, String? poster, SmartTextAlign alignment, int? blockIndex}) |
int |
Inserts a video block at or after blockIndex. |
updateVideo(int blockIndex, {...}) |
void |
Updates properties on an existing video block. |
removeVideo(int blockIndex) |
void |
Deletes the video block at blockIndex. |
getVideoNode(int blockIndex) |
VideoNode? |
Returns the VideoNode or null. |
insertAudio({required String src, String? title, String? author, SmartTextAlign alignment, int? blockIndex}) |
int |
Inserts an audio block at or after blockIndex. |
updateAudio(int blockIndex, {...}) |
void |
Updates properties on an existing audio block. |
removeAudio(int blockIndex) |
void |
Deletes the audio block at blockIndex. |
getAudioNode(int blockIndex) |
AudioNode? |
Returns the AudioNode or null. |
insertDocument({required String src, required String fileName, int? fileSize, String? fileExtension, SmartTextAlign alignment, int? blockIndex}) |
int |
Inserts a document file attachment block at or after blockIndex. |
updateDocument(int blockIndex, {...}) |
void |
Updates properties on an existing document block. |
removeDocument(int blockIndex) |
void |
Deletes the document block at blockIndex. |
getDocumentNode(int blockIndex) |
DocumentNode? |
Returns the DocumentNode or null. |
Device Picker Delegates
You can override how files are picked on the user's device by supplying delegates on controller:
final controller = SmartEditorController();
// 1. Pick images from camera/gallery
controller.imagePickerDelegate = () async {
return 'data:image/png;base64,...';
};
// 2. Pick videos from device
controller.videoPickerDelegate = () async {
return SmartAttachmentData(
src: '/path/to/video.mp4',
name: 'demo.mp4',
type: SmartAttachmentType.video,
);
};
// 3. Pick audio files from device
controller.audioPickerDelegate = () async {
return SmartAttachmentData(
src: '/path/to/song.mp3',
name: 'audio.mp3',
type: SmartAttachmentType.audio,
);
};
// 4. Pick documents (PDF, DOCX, ZIP)
controller.filePickerDelegate = () async {
return SmartAttachmentData(
src: '/path/to/doc.pdf',
name: 'Report.pdf',
fileSize: 1048576,
fileExtension: 'pdf',
type: SmartAttachmentType.document,
);
};
// Or use the unified attachment delegate for all types
controller.attachmentPickerDelegate = (SmartAttachmentType type) async {
// Return SmartAttachmentData based on type
return null;
};
Custom Media Player Builders
Easily connect your preferred third-party audio/video player (e.g. chewie, audioplayers, just_audio, video_player) through SmartEditorSettings:
SmartEditor(
controller: controller,
editorSettings: SmartEditorSettings(
// Custom video player builder
videoBuilder: (context, videoNode) {
return MyCustomVideoPlayer(
url: videoNode.src,
caption: videoNode.caption,
);
},
// Custom audio player builder
audioBuilder: (context, audioNode) {
return MyCustomAudioPlayer(
url: audioNode.src,
title: audioNode.title,
);
},
),
)
Toolbar Configuration
Configure attachment options in SmartToolbarSettings via SmartInsertButtons:
SmartToolbarSettings(
defaultButtons: [
const SmartInsertButtons(
link: true,
table: true,
attachment: true, // Enables the attachment button
enabledAttachments: {
SmartAttachmentType.image,
SmartAttachmentType.video,
SmartAttachmentType.audio,
SmartAttachmentType.document,
},
),
],
)
When clicked:
- If multiple attachment types are enabled,
SmartAttachmentBottomSheetopens with options. - If only one attachment type is enabled,
SmartAttachmentDialogopens directly. - The dialog allows remote URL imports, device file picking, captions, and optional cropping for images.
Interactive Image Block Widget
When an image block is tapped (selected), a contextual floating action toolbar appears with:
| Action | Icon | Description |
|---|---|---|
| Crop | βοΈ | Opens the native image_cropper to crop/adjust the existing image. |
| Align Left / Center / Right | βοΈ β¬ βΆοΈ | Changes horizontal alignment of the image block. |
| Resize | π | Width/height input fields to resize the image. |
| Remove | ποΈ | Deletes the image block from the document. |
Cropper Utility (SmartImageCropper)
The built-in cropper wraps the official image_cropper package:
| Method | Return Type | Description |
|---|---|---|
SmartImageCropper.crop({required BuildContext context, required String imageSrc, bool isDarkMode}) |
Future<String?> |
Crops an image using the native platform cropper. Accepts base64 Data URLs, network URLs, or local file paths. Returns the cropped image as a base64 Data URL, or null if the user cancelled. |
SmartImageCropper.show(context, {required String imageSrc, bool isDarkMode}) |
Future<String?> |
Convenience alias for crop(). |
Platform Dependencies
Add to your app's pubspec.yaml if utilizing device camera/gallery or file pickers:
dependencies:
flutter_smart_editor: ^2.3.0
# Transitive dependencies of flutter_smart_editor:
# - image_picker: Camera/Gallery access
# - image_cropper: Native cropping UI
# - file_picker: Document & file access
# - permission_handler: Runtime permission requests
iOS β add to ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is needed to take photos for the editor.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library access is needed to pick images for the editor.</string>
Android β add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
π οΈ Upcoming Features #
- β Markdown Shortcuts: Auto-format headers and lists during typing.
- β Find & Replace: Native search overlay with match highlighting.
- β Image Blocks: Support for network/local/camera images with cropping, resizing, and alignment.
- β Multi-Media Attachments: Native audio, video, and document blocks with custom media player builders.
- β Expandable Toolbar Polish: Clean single-toolbar toggling without duplicate rows or orphan separators.
- β Code Blocks: Syntax highlighting for 100+ languages.
- β Hyperlinks: Comprehensive link insertion and management dialogs.
- β Focus Mode: Zen mode for distraction-free writing.
- β Live Statistics: Real-time word, character, and reading time counters.
- β Auto-Save: Background persistence and draft recovery.
- β AI Assistant: context-aware writing improvements and summaries.
- β PDF Export: Generate high-quality PDFs directly from Dart.
- β Real-time Sync: Collaborative editing via WebSocket/CRDT.
- β Slash Commands: Notion-style
/menu for quick block insertion. - β Mobile Haptics: Tactile feedback for editing actions.
β Troubleshooting #
Failed to load 'libsuper_native_extensions.so' #
If you encounter errors when using the Paste feature:
flutter cleanflutter pub get- Perform a cold start (full rebuild) of the app. This is required to bundle the native clipboard libraries.
π License #
This project is licensed under the MIT License.








