squire_html_editor 1.0.0
squire_html_editor: ^1.0.0 copied to clipboard
HTML WYSIWYG rich text editor for Flutter with built-in speech-to-text.
HTML Editor (WYSIWYG, Rich Text) #
HTML editor for Flutter with built-in voice-to-text. Try it here
[Flutter Rich Text Editor Web]
Under the Hood #
This plugin is a reworked html_editor_enhanced with a few differences:
- Widget height: wrap content, expand, or explicit, with height ChangeNotifier.
- Summernote and jQuery replaced with Squire and DOMPurify for stricter security, HTML5 compatibility, features, performance and size.
- in_app_webview replaced with Flutter's own webview_flutter.
Basic Implementation #
Basic implementation of this editor doesn't require a controller. For simplicity and ease of use, [HtmlEditor] gives you access to the following top-level attributes:
height
(double
) - sets explicit height of the widgetminHeight
(double
) - sets minimum height of the widgetexpandFullHeight
(bool
) - sizes widget to take all available heighthint
(String
) - Displays hint when the field is emptyinitialValue
(String
) - initial HTML or textonChanged
(String
) - top-level shortcut toonChanged
callbackisReadOnly
(bool
) - locks the editor and removes the toolbarenableDictation
(bool
) - yay or nay to voice-to-text feature
import 'package:squire_html_editor/squire_html_editor.dart';
// ...
// 1. Define a var to store changes within parent class or a provider etc...
String result = 'Hello world!';
// ...
// 2. Add HtmlEditor to your build method
@override
Widget build(BuildContext context) =>
HtmlEditor(initalValue: result, onChanged:(s)=> result = s ?? '');
Advanced Implementation #
To take advantage of the entire API you'll need to create and configure an instance of [HtmlEditorController]. That instance provides access to the following groups of features:
- Styling options group (all things CSS, HTML and sanitizing)
- Toolbar options group (all things toolbar)
- Editor options group (all things editor)
When controller is provided, contents of the editor could be tried and accessed syncronously via a getter:
if(controller.contentIsNotEmpty){
Navigator.of(context).pop(controller.content);
}
HTML Styling Options #
The stylingOptions
parameter of [HtmlEditorController] class defines the look of generated HTML. Here you can select which tag to use for paragraphs and how your tags are styled.
var stylingOptions = HtmlStylingOptions(
// Adding global style is optional, but could be set in two ways:
// 1. by providing a CSS string to the parameter `globalStyleSheet`:
globalStyleSheet: '/* Your CSS string contents of style.css file */',
// This defines which tag to use for paragraphs.
// The default value is `p`, however the `div` is also acceptable.
blockTag: 'p',
// defines `style` and `class` attributes of a block tag
blockTagAttributes: HtmlTagAttributes(
// this is added as inline CSS for every tag
inlineStyle: 'text-indent:3.5em; text-align:justify;',
// defines `class` attribute value of every tag
cssClass: 'my-custom-pgf'),
// next we can define attributes for other tags (li, ul, ol, a etc):
li: HtmlTagAttributes(
inlineStyle: 'margin: .5em 1em .5em .5em',
cssClass: 'my-custom-li-class'),
// ... other HTML tag definitions ... //
code: HtmlTagAttributes(
inlineStyle: 'padding: .5em 1em;', cssClass: 'my-custom-li-class'),
// when sanitizeOnPaste is `true` - editor will strip all
// HTML pasted into the editor down to plain text
sanitizeOnPaste: true,
);
// 2. another way to add global CSS is to call this async method:
await stylingOptions.importCssFromFile('path/to/style.css');
// ...
// Now create the editor passing the styling options
return HtmlEditor(
controller: HtmlEditorController(stylingOptions: stylingOptions),
onChanged: (p0) => (p0) {/* TODO */},
initialValue: '' /* TODO */,
);
The code above should result in the following HTML being generated for each paragraph:
<p style="text-indent:3.5em; text-align:justify;" class="my-custom-pgf"></p>
Sizing and Constraints #
By default, the widget occupies all available width and sizes its height based on the height of its content, but not less than the value of minHeight
attribute of [HtmlEditor] widget.
// since explicit height is not provided - the editor will size itself
// based on content, but will be not less than 250px
return HtmlEditor(
controller: controller,
// ...
minHeight: 250, // should be not less than 64px
// ...
);
// and here you can listen to changes in height of the editor
ValueListenableBuilder<double>(
valueListenable: controller.totalHeight,
builder: (BuildContext context, double value, Widget? child) {
return Text('Height changed to $value\n'
'Toolbar height is ${controller.toolbarHeight}\n'
'Content height is ${controller.contentHeight}\n' );
});
If explicit `height` is provided - the widget will size it's height precisely to the value of `height` attribute. In this case, if content height is greater than the widget height - the content becomes scrollable.
// here we've provided the height value, so the editor will always be
// that height and the content will scroll if overflows the height.
return HtmlEditor(
height: 250,
);
If `expandFullHeight` is set to `true` - the widget will take up all available height.
return HtmlEditor(
expandFullHeight: true,
);
Toolbar Position #
All toolbar-related options are contained within [ToolbarOptions] of [HtmlEditorController] class. Toolbar could be positionned:
- above, below the editor container, by setting the
toolbarPosition
attribute;
Above editor:
[Toolbar above editor]
Below editor:
[Toolbar below editor]
-
detached from the editor and located anywhere outside the [HtmlEditor]widget. This allows [ToolbarWidget] to be attached to several HtmlEditors. For this type of implementation please refer to the example within the package. [Toolbar floating]
-
scrollable, grid or expandable by setting the
toolbarType
attribute
Toolbar Contents and Custom Button Groups #
Toolbar button groups could be enabled/disabled via defaultToolbarButtons
attribute of [HtmlToolbarOptions] class within the controller. You can customize the toolbar by overriding the default value of this attribute.
Adding your own button groups to the toolbar is very simple - just provide a list of [CustomButtonGroup] objects to the customButtonGroups
attribute. Each button group will consist of a list of [CustomToolbarButton] objects, each with its own icon, tap callback and an isSelected
flag to let the toolbar know if the icon button should be highlighted.
HtmlEditor(
controller: HtmlEditorController()
..toolbarOptions.customButtonGroups = [
CustomButtonGroup(
index: 0, // place first
buttons: [
CustomToolbarButton(
icon: Icons.save_outlined,
action: () => /* TODO: implement your save method */,
isSelected: false)
])
],
),
= [Custom button]
Voice to Text (Dictation) #
Voice-to-text feature is powered by speech_to_text package and comes enabled by default with this package.
To disable voice-to-text feature - set the corresponding top-level enableDictation
attribute within [HtmlEditor] constructor to false
.
Overriding controller.toolbarOptions.defaultToolbarButtons
value also overrides enableDictation
flag (obviously), so you need to add const VoiceToTextButtons()
in order to keep seeing the voice-to-text button.
Special Considerations and Gotchas #
-
Due to some framework issues on Web, this plugin is only compatible with Flutter 3.3. If you want to use this plugin with earlier versions of Flutter - downgrade pointer_interceptor in this dependency to 0.9.0+1.
-
Following needs to be done to make things work on each platform:
Android #
For speech recognition to work - place this to android > app > src > main > AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application
...
iOS #
For speech recognition to work - add following permission to your Info.plist
file:
<key>NSSpeechRecognitionUsageDescription</key>
<string>recognize speech</string>
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for uploading videos</string>
Web Platform #
To get the toolbar to scroll horizontally on Web, you will need to make sure you override the default scroll behavior:
-
Add the following class override to your app:
class MyCustomScrollBehavior extends MaterialScrollBehavior { @override Set<PointerDeviceKind> get dragDevices => { PointerDeviceKind.touch, PointerDeviceKind.mouse, }; }
-
Add the following attribute to the [MaterialApp] widget:
return MaterialApp( // ... scrollBehavior: MyCustomScrollBehavior(), // ... );
Done. Now you should be able to drag the toolbar left and right on web.