import 'dart:async'; import 'dart:collection'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:html_editor_enhanced/html_editor.dart'; /// Options that modify the editor and its behavior class HtmlEditorOptions { const HtmlEditorOptions({ this.autoAdjustHeight = true, this.androidUseHybridComposition = true, this.adjustHeightForKeyboard = true, this.characterLimit, this.customOptions = '', this.darkMode, this.disabled = false, this.filePath, this.hint, this.initialText, this.inputType = HtmlInputType.text, this.mobileContextMenu, this.mobileLongPressDuration, this.mobileInitialScripts, this.webInitialScripts, this.shouldEnsureVisible = false, this.spellCheck = false, }); /// The editor will automatically adjust its height when the keyboard is active /// to prevent the keyboard overlapping the editor. /// /// The default value is true. It is recommended to leave this as true because /// it significantly improves the UX. final bool adjustHeightForKeyboard; /// ALlows devs to set hybrid composition off in case they would like to /// prioritize animation smoothness over text input experience. /// /// The recommended value is `true`. final bool androidUseHybridComposition; /// The editor will automatically adjust its height once the page is loaded to /// ensure there is no vertical scrolling or empty space. It will only perform /// the adjustment when the summernote editor is the loaded page. /// /// It will also disable vertical scrolling on the webview, so scrolling on /// the webview will actually scroll the rest of the page rather than doing /// nothing because it is trying to scroll the webview container. /// /// The default value is true. It is recommended to leave this as true because /// it significantly improves the UX. final bool autoAdjustHeight; /// Adds a character limit to the editor. /// /// NOTE: ONLY WORKS ON iOS AND WEB PLATFORMS!! final int? characterLimit; /// Set custom options for the summernote editor by using their syntax. /// /// Please ensure your syntax is correct (and add a comma at the end of your /// string!) otherwise the editor may not load. final String customOptions; /// Sets the editor to dark mode. `null` - switches with system, `false` - /// always light, `true` - always dark. /// /// The default value is null (switches with system). final bool? darkMode; /// Disable the editor immediately after startup. You can re-enable the editor /// by calling [controller.enable()]. final bool disabled; /// Specify the file path to your custom html editor code. /// /// Make sure to set the editor's HTML ID to be 'summernote-2'. /// /// If you plan to use this on Web, you must add comments in your HTML so the /// package can insert the relevant JS code to communicate between Dart and JS. /// See the README for more details on this. final String? filePath; /// Sets the Html editor's hint (text displayed when there is no text in the /// editor). final String? hint; /// The initial text that is be supplied to the Html editor. final String? initialText; /// Changes the display of the virtual keyboard on mobile devices. /// /// See [HtmlInputType] for the supported modes. /// /// The default value is [HtmlInputType.text] (the standard virtual keyboard) final HtmlInputType inputType; /// Customize the context menu for selected text on mobile final ContextMenu? mobileContextMenu; /// Set the duration until a long-press is recognized. /// /// The default value is 500ms. final Duration? mobileLongPressDuration; /// Initial JS to inject into the editor. final UnmodifiableListView? mobileInitialScripts; /// Initial JS to add to the editor. These can be called at any time using /// [controller.evaluateJavascriptWeb] final UnmodifiableListView? webInitialScripts; /// Specifies whether the widget should scroll to reveal the HTML editor when /// it is focused or the text content is changed. /// See the README examples for the best way to implement this. /// /// Note: Your editor *must* be in a Scrollable type widget (e.g. ListView, /// SingleChildScrollView, etc.) for this to work. Otherwise, nothing will /// happen. final bool shouldEnsureVisible; /// Specify whether or not the editor should spellcheck its contents. /// /// Default value is false. final bool spellCheck; } /// Options that modify the toolbar and its behavior class HtmlToolbarOptions { const HtmlToolbarOptions({ this.audioExtensions, this.customToolbarButtons = const [], this.customToolbarInsertionIndices = const [], this.defaultToolbarButtons = const [ StyleButtons(), FontSettingButtons(fontSizeUnit: false), FontButtons(clearAll: false), ColorButtons(), ListButtons(listStyles: false), ParagraphButtons( textDirection: false, lineHeight: false, caseConverter: false), InsertButtons( video: false, audio: false, table: false, hr: false, otherFile: false), ], this.otherFileExtensions, this.imageExtensions, this.initiallyExpanded = false, this.linkInsertInterceptor, this.mediaLinkInsertInterceptor, this.mediaUploadInterceptor, this.onButtonPressed, this.onDropdownChanged, this.onOtherFileLinkInsert, this.onOtherFileUpload, this.toolbarType = ToolbarType.nativeScrollable, this.toolbarPosition = ToolbarPosition.aboveEditor, this.videoExtensions, this.dropdownElevation = 8, this.dropdownIcon, this.dropdownIconColor, this.dropdownIconSize = 24, this.dropdownItemHeight = kMinInteractiveDimension, this.dropdownFocusColor, this.dropdownBackgroundColor, this.dropdownMenuDirection, this.dropdownMenuMaxHeight, this.dropdownBoxDecoration, this.buttonColor, this.buttonSelectedColor, this.buttonFillColor, this.buttonFocusColor, this.buttonHighlightColor, this.buttonHoverColor, this.buttonSplashColor, this.buttonBorderColor, this.buttonSelectedBorderColor, this.buttonBorderRadius, this.buttonBorderWidth, this.renderBorder = false, this.textStyle, this.separatorWidget = const VerticalDivider(indent: 2, endIndent: 2, color: Colors.grey), this.renderSeparatorWidget = true, this.toolbarItemHeight = 36, this.gridViewHorizontalSpacing = 5, this.gridViewVerticalSpacing = 5, }); /// Allows you to set the allowed extensions when a user inserts an audio file /// /// By default any audio extension is allowed. final List? audioExtensions; /// Allows you to create your own buttons that are added to the end of the /// default buttons list final List customToolbarButtons; /// Allows you to set where each custom toolbar button is inserted into the /// toolbar buttons. /// /// Notes: 1) This list should have the same length as the [customToolbarButtons] /// /// 2) If any indices > [defaultToolbarButtons.length] then the plugin will /// automatically account for this and insert the buttons at the end of the /// [defaultToolbarButtons] /// /// 3) If any indices < 0 then the plugin will automatically account for this /// and insert the buttons at the beginning of the [defaultToolbarButtons] final List customToolbarInsertionIndices; /// Sets which options are visible in the toolbar for the editor. final List defaultToolbarButtons; /// Allows you to set the allowed extensions when a user inserts an image /// /// By default any image extension is allowed. final List? imageExtensions; /// Allows you to set whether the toolbar starts out expanded (in gridview) /// or contracted (in scrollview). /// /// By default it starts out contracted. /// /// This option only works when you have set [toolbarType] to /// [ToolbarType.nativeExpandable]. final bool initiallyExpanded; /// Allows you to intercept any links being inserted into the editor. The /// function passes the display text, the URL itself, and whether the /// URL should open a new tab. /// /// Return a bool to tell the plugin if it should continue with its own handler /// or if you want to handle the link by yourself. /// (true = continue with internal handler, false = do not use internal handler) /// /// If no interceptor is set, the plugin uses the internal handler. final FutureOr Function(String, String, bool)? linkInsertInterceptor; /// Allows you to intercept any image/video/audio inserted as a link into the editor. /// The function passes the URL of the media inserted. /// /// Return a bool to tell the plugin if it should continue with its own handler /// or if you want to handle the image/video link by yourself. /// (true = continue with internal handler, false = do not use internal handler) /// /// If no interceptor is set, the plugin uses the internal handler. final FutureOr Function(String, InsertFileType)? mediaLinkInsertInterceptor; /// Allows you to intercept any image/video/audio files being inserted into the editor. /// The function passes the PlatformFile class, which contains all the file data /// including name, size, type, Uint8List bytes, etc. /// /// Return a bool to tell the plugin if it should continue with its own handler /// or if you want to handle the image/video/audio upload by yourself. /// (true = continue with internal handler, false = do not use internal handler) /// /// If no interceptor is set, the plugin uses the internal handler. final FutureOr Function(PlatformFile, InsertFileType)? mediaUploadInterceptor; /// Allows you to intercept any button press. The function passes the ButtonType /// enum, which tells you which button was pressed, the current selected status of /// the button, and a function to reverse the status (in case you decide to handle /// the button press yourself). /// /// Note: In some cases, the button is never active (e.g. copy/paste buttons) /// so null will be returned for both the selected status and the function. /// /// Return a bool to tell the plugin if it should continue with its own handler /// or if you want to handle the button press by yourself. /// (true = continue with internal handler, false = do not use internal handler) /// /// If no interceptor is set, the plugin uses the internal handler. final FutureOr Function(ButtonType, bool?, Function?)? onButtonPressed; /// Allows you to intercept any dropdown changes. The function passes the /// DropdownType enum, which tells you which dropdown was changed, /// the changed value to indicate what the dropdown was changed to, and the /// function to update the changed value (in case you decide to handle the /// dropdown change yourself). The function is null in some cases because /// the dropdown does not update its value. /// /// Return a bool to tell the plugin if it should continue with its own handler /// or if you want to handle the dropdown change by yourself. /// (true = continue with internal handler, false = do not use internal handler) /// /// If no interceptor is set, the plugin uses the internal handler. final FutureOr Function(DropdownType, dynamic, void Function(dynamic)?)? onDropdownChanged; /// Called when a link is inserted for a file using the "other file" button. /// /// The package does not have a built in handler for these files, so you should /// provide this callback when using the button. /// /// The function passes the URL of the file inserted. final void Function(String)? onOtherFileLinkInsert; /// Called when a file is uploaded using the "other file" button. /// /// The package does not have a built in handler for these files, so if you use /// the button you should provide this callback. /// /// The function passes the PlatformFile class, which contains all the file data /// including name, size, type, Uint8List bytes, etc. final void Function(PlatformFile)? onOtherFileUpload; /// Allows you to set the allowed extensions when a user inserts a file other /// than image/audio/video /// /// By default any other extension is allowed. final List? otherFileExtensions; /// Controls how the toolbar displays. See [ToolbarType] for more details. /// /// By default the toolbar is rendered as a scrollable one-line list. final ToolbarType toolbarType; /// Controls where the toolbar is positioned. See [ToolbarPosition] for more details. /// /// By default the toolbar is above the editor. final ToolbarPosition toolbarPosition; /// Allows you to set the allowed extensions when a user inserts a video. /// /// By default any video extension is allowed. final List? videoExtensions; /// Styling options for the toolbar: /// Determines whether a border is rendered around all toolbar widgets /// /// The default value is false. True is recommended for [ToolbarType.nativeGrid]. final bool renderBorder; /// Sets the text style for all toolbar widgets final TextStyle? textStyle; /// Sets the separator widget between toolbar sections. This widget is only /// used in [ToolbarType.nativeScrollable]. /// /// The default widget is [VerticalDivider(indent: 2, endIndent: 2, color: Colors.grey)] final Widget separatorWidget; /// Determines whether the separator widget is rendered /// /// The default value is true final bool renderSeparatorWidget; /// Sets the height of the toolbar items /// /// Button width is affected by this parameter, however dropdown widths are /// not affected. The plugin will maintain a square shape for all buttons. /// /// The default value is 36 final double toolbarItemHeight; /// Sets the vertical spacing between rows when using [ToolbarType.nativeGrid] /// /// The default value is 5 final double gridViewVerticalSpacing; /// Sets the horizontal spacing between items when using [ToolbarType.nativeGrid] /// /// The default value is 5 final double gridViewHorizontalSpacing; /// Styling options that only apply to dropdowns: /// (See the [DropdownButton] class for more information) final int dropdownElevation; final Widget? dropdownIcon; final Color? dropdownIconColor; final double dropdownIconSize; final double dropdownItemHeight; final Color? dropdownFocusColor; final Color? dropdownBackgroundColor; /// Set the menu opening direction for the dropdown. Only useful when using /// [ToolbarPosition.custom] since the toolbar otherwise automatically /// determines the correct direction. final DropdownMenuDirection? dropdownMenuDirection; final double? dropdownMenuMaxHeight; final BoxDecoration? dropdownBoxDecoration; /// Styling options that only apply to the buttons: /// (See the [ToggleButtons] class for more information) final Color? buttonColor; final Color? buttonSelectedColor; final Color? buttonFillColor; final Color? buttonFocusColor; final Color? buttonHighlightColor; final Color? buttonHoverColor; final Color? buttonSplashColor; final Color? buttonBorderColor; final Color? buttonSelectedBorderColor; final BorderRadius? buttonBorderRadius; final double? buttonBorderWidth; } /// Other options such as the height of the widget and the decoration surrounding it class OtherOptions { const OtherOptions({ this.decoration = const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(4)), border: Border.fromBorderSide(BorderSide(color: Color(0xffececec), width: 1)), ), this.height = 400, }); /// The BoxDecoration to use around the Html editor. By default, the widget /// uses a thin, dark, rounded rectangle border around the widget. final BoxDecoration decoration; /// Sets the height of the Html editor widget. This takes the toolbar into /// account (i.e. this sets the height of the entire widget rather than the /// editor space) /// /// The default value is 400. final double height; }