A Summernote-based rich-text HTML editor for Flutter on Android, iOS, and web.
Version 1.0.1 embeds Summernote Lite 0.9.1 and uses Summernote's own toolbar on every supported platform. The previous Flutter-native toolbar API has been removed.
summernote_editor is a maintained successor to
html_editor_enhanced. The original project is
Copyright (c) 2020 Tanay Neotia and remains acknowledged under the included MIT license.
- One editor API for Android, iOS, and Flutter web
- Summernote Lite toolbar, dialogs, code view, tables, links, images, and video
- Content-based height by default, with no nested vertical editor scroll
- Optional fixed-height mode
- Controller commands and Summernote callbacks
- Initial HTML, character limits, spellcheck, custom Summernote options, and custom templates
- Mobile and web JavaScript hooks
- Optional Summernote at-mention plugin
Add the package to your pubspec.yaml:
dependencies:
summernote_editor: ^1.0.1Then import the public library:
import 'package:summernote_editor/summernote_editor.dart';For Android, make sure the app has internet permission if the editor will load remote images, links, or videos:
<uses-permission android:name="android.permission.INTERNET" />Place the editor in the same Flutter scrollable as the surrounding content. With
autoAdjustHeight: true (the default), the editor grows and shrinks with its content, and the
parent Flutter widget owns vertical scrolling.
final controller = SummernoteEditorController();
SingleChildScrollView(
child: Column(
children: [
const Text('Article'),
SummernoteEditor(
controller: controller,
summernoteEditorOptions: const SummernoteEditorOptions(
hint: 'Write something…',
shouldEnsureVisible: true,
),
otherOptions: const OtherOptions(height: 400),
callbacks: Callbacks(
onChangeContent: (html) => debugPrint(html),
),
),
],
),
)OtherOptions.height is the temporary loading height in content-height mode. When
autoAdjustHeight is false, it is the total fixed height of the Summernote toolbar and editable
area.
The built-in Summernote Lite toolbar is enabled automatically. Its default configuration includes
all built-in style, font, color, paragraph, insertion, and history controls except the complete
view group and the video and table insertion controls. It runs inside the editor document, so
toolbar state, dropdowns, dialogs, and selection handling stay within Summernote instead of being
duplicated by Flutter widgets.
Customize its groups with Summernote's toolbar option through customOptions:
SummernoteEditor(
controller: controller,
summernoteEditorOptions: const SummernoteEditorOptions(
customOptions: '''
toolbar: [
['font', ['bold', 'italic', 'underline', 'clear']],
['para', ['ul', 'ol', 'paragraph']],
['insert', ['link', 'picture']]
],
''',
),
)customOptions is inserted after the package defaults, so a supplied toolbar property replaces
the default toolbar. Non-empty values must use valid Summernote syntax and end with a comma. The
package always disables Summernote's manual resize handle; Flutter or the surrounding layout
controls the editor size.
In content-height mode, the editor temporarily expands while a Summernote dropdown, popover, or dialog is visible so the overlay is not clipped by the iframe/WebView. It returns to content height when the overlay closes.
Use a fixed-height editor only when a nested editor scroll is intentional:
SummernoteEditor(
controller: controller,
summernoteEditorOptions: const SummernoteEditorOptions(
autoAdjustHeight: false,
adjustHeightForKeyboard: true,
),
otherOptions: const OtherOptions(height: 420),
)Create one controller per editor. Common operations include:
final html = await controller.getText();
controller.setText('<p>Hello</p>');
controller.insertText('Plain text');
controller.insertHtml('<strong>HTML</strong>');
controller.insertLink('OpenAI', 'https://openai.com', true);
controller.insertNetworkImage('https://example.com/image.png');
controller.undo();
controller.redo();
controller.clear();
controller.setFocus();
controller.toggleCodeView();
controller.disable();
controller.enable();Other available commands include execCommand, setHint, setFullScreen,
recalculateHeight, addNotification, removeNotification, getSelectedTextWeb,
evaluateJavascriptWeb, reloadWeb, clearFocus, and resetHeight. Platform-specific methods
should be guarded with kIsWeb where appropriate. On mobile, issue startup commands from
Callbacks.onInit, after the WebView has initialized.
SummernoteEditorOptions supports:
autoAdjustHeight: follow rendered toolbar and content height; defaults totrueadjustHeightForKeyboard: resize fixed-height mobile editors around the keyboardandroidUseHybridComposition: Android WebView composition modecharacterLimit: maximum text character countcustomOptions: raw Summernote initialization propertiesdisabled: start with editing and toolbar controls disabledfilePath: custom HTML templatehintandinitialTextinputType: mobile virtual keyboard modemobileContextMenu,mobileInitialScripts, andmobileLongPressDurationwebInitialScriptsshouldEnsureVisible: reveal the editor when it receives focusspellCheck
OtherOptions contains the editor height and outer decoration.
Use Callbacks to observe Summernote and bridge events:
callbacks: Callbacks(
onInit: () => debugPrint('ready'),
onChangeContent: (html) => debugPrint('changed: $html'),
onFocus: () => debugPrint('focused'),
onBlur: () => debugPrint('blurred'),
onDialogShown: () => debugPrint('dialog opened'),
onChangeSelection: (settings) {
debugPrint('font: ${settings.fontName}');
},
),Image callbacks intentionally replace Summernote's default insertion behavior. When using
onImageLinkInsert or onImageUpload, insert the resulting image yourself with the controller if
that is the desired behavior.
plugins: [
SummernoteAtMention(
getSuggestionsMobile: (query) =>
['alice', 'bob'].where((name) => name.contains(query)).toList(),
mentionsWeb: const ['alice', 'bob'],
onSelect: (name) => debugPrint('selected $name'),
),
],Mobile suggestions are produced by a Dart callback. Web suggestions are filtered from the static
mentionsWeb list inside the iframe.
The bundled example is intentionally small enough to use as a starting point. It demonstrates two
independent editors on one responsive page, initial HTML with an embedded image, per-editor
enable/disable actions, safe controller use after onInit, and concurrent reads.
Run it with:
cd example
flutter run -d chromeThe editor element must have the ID summernote-2. Flutter web templates must also retain these
injection markers:
<head>
<!--headString-->
</head>
<body>
<div id="summernote-2"></div>
<!--summernoteScripts-->
</body>Declare the custom template as a Flutter asset and pass its path through
SummernoteEditorOptions.filePath. The template must load compatible jQuery and Summernote assets.
Summernote Lite 0.9.1 is vendored under lib/assets. Source and checksums are recorded in
lib/assets/SUMMERNOTE.md.
- The editor is backed by
flutter_inappwebviewon Android and iOS. - Flutter web uses an iframe platform view.
- There is currently no automated integration-test suite. Changes involving WebView/iframe input, toolbar dialogs, file selection, keyboard behavior, or scrolling should be checked on the target platforms.
This package is licensed under the MIT License. See LICENSE.