added project'
This commit is contained in:
187
lib/utils/callbacks.dart
Normal file
187
lib/utils/callbacks.dart
Normal file
@@ -0,0 +1,187 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:html_editor_enhanced/html_editor.dart';
|
||||
|
||||
/// Manages all the callback functions the library provides
|
||||
class Callbacks {
|
||||
Callbacks({
|
||||
this.onBeforeCommand,
|
||||
this.onChangeContent,
|
||||
this.onChangeCodeview,
|
||||
this.onChangeSelection,
|
||||
this.onDialogShown,
|
||||
this.onEnter,
|
||||
this.onFocus,
|
||||
this.onBlur,
|
||||
this.onBlurCodeview,
|
||||
this.onImageLinkInsert,
|
||||
this.onImageUpload,
|
||||
this.onImageUploadError,
|
||||
this.onInit,
|
||||
this.onKeyUp,
|
||||
this.onKeyDown,
|
||||
this.onMouseUp,
|
||||
this.onMouseDown,
|
||||
this.onNavigationRequestMobile,
|
||||
this.onPaste,
|
||||
this.onScroll,
|
||||
});
|
||||
|
||||
/// Called before certain commands are fired and the editor is in rich text view.
|
||||
/// There is currently no documentation on this parameter, thus it is
|
||||
/// unclear which commands this will fire before.
|
||||
///
|
||||
/// This function will return the current HTML in the editor as an argument.
|
||||
void Function(String?)? onBeforeCommand;
|
||||
|
||||
/// Called whenever the HTML content of the editor is changed and the editor
|
||||
/// is in rich text view.
|
||||
///
|
||||
/// Note: This function also seems to be called if input is detected in the
|
||||
/// editor field but the content does not change.
|
||||
/// E.g. repeatedly pressing backspace when the field is empty
|
||||
/// will also trigger this callback.
|
||||
///
|
||||
/// This function will return the current HTML in the editor as an argument.
|
||||
void Function(String?)? onChangeContent;
|
||||
|
||||
/// Called whenever the code of the editor is changed and the editor
|
||||
/// is in code view.
|
||||
///
|
||||
/// Note: This function also seems to be called if input is detected in the
|
||||
/// editor field but the content does not change.
|
||||
/// E.g. repeatedly pressing backspace when the field is empty
|
||||
/// will also trigger this callback.
|
||||
///
|
||||
/// This function will return the current code in the codeview as an argument.
|
||||
void Function(String?)? onChangeCodeview;
|
||||
|
||||
/// Called whenever the selection area of the editor is changed.
|
||||
///
|
||||
/// It passes all the editor settings at the current selection as an argument.
|
||||
/// This can be used in custom toolbar item implementations, to update your
|
||||
/// toolbar item UI when the editor formatting changes.
|
||||
void Function(EditorSettings)? onChangeSelection;
|
||||
|
||||
/// Called whenever a dialog is shown in the editor. The dialogs will be either
|
||||
/// the link, image, video, or help dialogs.
|
||||
void Function()? onDialogShown;
|
||||
|
||||
/// Called whenever the enter/return key is pressed and the editor
|
||||
/// is in rich text view. There is currently no way to detect enter/return
|
||||
/// when the editor is in code view.
|
||||
///
|
||||
/// Note: The onChange callback will also be triggered at the same time as
|
||||
/// this callback, please design your implementation accordingly.
|
||||
void Function()? onEnter;
|
||||
|
||||
/// Called whenever the rich text field gains focus. This will not be called
|
||||
/// when the code view editor gains focus, instead use [onBlurCodeview] for
|
||||
/// that.
|
||||
void Function()? onFocus;
|
||||
|
||||
/// Called whenever either the rich text field or the codeview field loses
|
||||
/// focus. This will also be triggered when switching from the rich text editor
|
||||
/// to the code view editor.
|
||||
///
|
||||
/// Note: Due to the current state of webviews in Flutter, tapping outside
|
||||
/// the webview or dismissing the keyboard does not trigger this callback.
|
||||
/// This callback will only be triggered if the user taps on an empty space
|
||||
/// in the toolbar or switches the view mode of the editor.
|
||||
void Function()? onBlur;
|
||||
|
||||
/// Called whenever the code view either gains or loses focus (the Summernote
|
||||
/// docs say this will only be called when the code view loses focus but
|
||||
/// in my testing this is not the case). This will also be triggered when
|
||||
/// switching between the rich text editor and the code view editor.
|
||||
///
|
||||
/// Note: Due to the current state of webviews in Flutter, tapping outside
|
||||
/// the webview or dismissing the keyboard does not trigger this callback.
|
||||
/// This callback will only be triggered if the user taps on an empty space
|
||||
/// in the toolbar or switches the view mode of the editor.
|
||||
void Function()? onBlurCodeview;
|
||||
|
||||
/// Called whenever an image is inserted via a link. The function passes the
|
||||
/// URL of the image inserted into the editor.
|
||||
///
|
||||
/// Note: Setting this function overrides the default summernote image via URL
|
||||
/// insertion handler! This means you must manually insert the image using
|
||||
/// [controller.insertNetworkImage] in your callback function, otherwise
|
||||
/// nothing will be inserted into the editor!
|
||||
void Function(String?)? onImageLinkInsert;
|
||||
|
||||
/// Called whenever an image is inserted via upload. The function passes the
|
||||
/// [FileUpload] class, containing the filename, size, MIME type, base64 data,
|
||||
/// and last modified information so you can upload it into your server.
|
||||
///
|
||||
/// Note: Setting this function overrides the default summernote upload image
|
||||
/// insertion handler (base64 handler)! This means you must manually insert
|
||||
/// the image using [controller.insertNetworkImage] (for uploaded images) or
|
||||
/// [controller.insertHtml] (for base64 data) in your callback function,
|
||||
/// otherwise nothing will be inserted into the editor!
|
||||
void Function(FileUpload)? onImageUpload;
|
||||
|
||||
/// Called whenever an image is failed to be inserted via upload. The function
|
||||
/// passes the [FileUpload] class, containing the filename, size, MIME type,
|
||||
/// base64 data, and last modified information so you can do error handling.
|
||||
void Function(FileUpload?, String?, UploadError)? onImageUploadError;
|
||||
|
||||
/// Called whenever [InAppWebViewController.onLoadStop] is fired on mobile
|
||||
/// or when the [IFrameElement.onLoad] stream is fired on web. Note that this
|
||||
/// method will also be called on refresh on both platforms.
|
||||
///
|
||||
/// You can use this method to set certain properties - e.g. set the editor
|
||||
/// to fullscreen mode - as soon as the editor is ready to accept these commands.
|
||||
void Function()? onInit;
|
||||
|
||||
/// Called whenever a key is released and the editor is in rich text view.
|
||||
///
|
||||
/// This function will return the keycode for the released key as an argument.
|
||||
///
|
||||
/// Note: The keycode [is broken](https://stackoverflow.com/questions/36753548/keycode-on-android-is-always-229)
|
||||
/// on Android, you will only ever receive 229, 8 (backspace), or 13 (enter)
|
||||
/// as a keycode. 8 and 13 only seem to be returned when the editor is empty
|
||||
/// and those keys are released.
|
||||
void Function(int?)? onKeyUp;
|
||||
|
||||
/// Called whenever a key is downed and the editor is in rich text view.
|
||||
///
|
||||
/// This function will return the keycode for the downed key as an argument.
|
||||
///
|
||||
/// Note: The keycode [is broken](https://stackoverflow.com/questions/36753548/keycode-on-android-is-always-229)
|
||||
/// on Android, you will only ever receive 229, 8 (backspace), or 13 (enter)
|
||||
/// as a keycode. 8 and 13 only seem to be returned when the editor is empty
|
||||
/// and those keys are downed.
|
||||
void Function(int?)? onKeyDown;
|
||||
|
||||
/// Called whenever the mouse/finger is released and the editor is in rich text view.
|
||||
void Function()? onMouseUp;
|
||||
|
||||
/// Called whenever the mouse/finger is downed and the editor is in rich text view.
|
||||
void Function()? onMouseDown;
|
||||
|
||||
/// Called right before the URL of the webview is changed on mobile. This allows
|
||||
/// you to prevent URLs from loading, or launch them externally, for example.
|
||||
///
|
||||
/// This function passes the URL to be loaded, and you must return a
|
||||
/// `NavigationActionPolicy` to tell the webview what to do.
|
||||
FutureOr<NavigationActionPolicy> Function(String)? onNavigationRequestMobile;
|
||||
|
||||
/// Called whenever text is pasted into the rich text field. This will not be
|
||||
/// called when text is pasted into the code view editor.
|
||||
///
|
||||
/// Note: This will not be called when programmatically inserting HTML into
|
||||
/// the editor with [HtmlEditor.insertHtml].
|
||||
void Function()? onPaste;
|
||||
|
||||
/// Called whenever the editor is scrolled and it is in rich text view.
|
||||
/// Editor scrolled is considered to be the editor box only, not the webview
|
||||
/// container itself. Thus, this callback will only fire when the content in
|
||||
/// the editor is longer than the editor height. This function can be called
|
||||
/// with an explicit scrolling action via the mouse, or also via implied
|
||||
/// scrolling, e.g. the enter key scrolling the editor to make new text visible.
|
||||
///
|
||||
/// Note: This function will be repeatedly called while the editor is scrolled.
|
||||
/// Make sure to factor that into your implementation.
|
||||
void Function()? onScroll;
|
||||
}
|
||||
59
lib/utils/file_upload_model.dart
Normal file
59
lib/utils/file_upload_model.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// Function that creates an instance of [FileUpload] from a JSON string
|
||||
FileUpload fileUploadFromJson(String str) =>
|
||||
FileUpload.fromJson(json.decode(str));
|
||||
|
||||
/// The [FileUpload] class stores any known data about a file. This class is used
|
||||
/// as an argument in some callbacks relating to image and file insertion.
|
||||
///
|
||||
/// The class holds last modified information, name, size, type, and the base64
|
||||
/// of the file.
|
||||
///
|
||||
/// Note that all parameters are nullable to prevent any null-exception when
|
||||
/// getting file data from JavaScript.
|
||||
class FileUpload {
|
||||
FileUpload({
|
||||
this.base64,
|
||||
this.lastModified,
|
||||
this.lastModifiedDate,
|
||||
this.name,
|
||||
this.size,
|
||||
this.type,
|
||||
});
|
||||
|
||||
/// The base64 string of the file.
|
||||
///
|
||||
/// Note: This includes identifying data (e.g. data:image/jpeg;base64,) at the
|
||||
/// beginning. To strip this out, use FileUpload().base64.split(",")[1].
|
||||
String? base64;
|
||||
|
||||
/// Last modified information in *milliseconds since epoch* format
|
||||
DateTime? lastModified;
|
||||
|
||||
/// Last modified information in *regular date* format
|
||||
DateTime? lastModifiedDate;
|
||||
|
||||
/// The filename
|
||||
String? name;
|
||||
|
||||
/// The file size in bytes
|
||||
int? size;
|
||||
|
||||
/// The content-type (eg. image/jpeg) of the file
|
||||
String? type;
|
||||
|
||||
/// Creates an instance of [FileUpload] from a JSON string
|
||||
factory FileUpload.fromJson(Map<String, dynamic> json) => FileUpload(
|
||||
base64: json['base64'],
|
||||
lastModified: json['lastModified'] == null
|
||||
? null
|
||||
: DateTime.fromMillisecondsSinceEpoch(json['lastModified']),
|
||||
lastModifiedDate: json['lastModifiedDate'] == null
|
||||
? null
|
||||
: DateTime.tryParse(json['lastModifiedDate']),
|
||||
name: json['name'],
|
||||
size: json['size'],
|
||||
type: json['type'],
|
||||
);
|
||||
}
|
||||
429
lib/utils/options.dart
Normal file
429
lib/utils/options.dart
Normal file
@@ -0,0 +1,429 @@
|
||||
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<UserScript>? mobileInitialScripts;
|
||||
|
||||
/// Initial JS to add to the editor. These can be called at any time using
|
||||
/// [controller.evaluateJavascriptWeb]
|
||||
final UnmodifiableListView<WebScript>? 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<String>? audioExtensions;
|
||||
|
||||
/// Allows you to create your own buttons that are added to the end of the
|
||||
/// default buttons list
|
||||
final List<Widget> 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<int> customToolbarInsertionIndices;
|
||||
|
||||
/// Sets which options are visible in the toolbar for the editor.
|
||||
final List<Toolbar> defaultToolbarButtons;
|
||||
|
||||
/// Allows you to set the allowed extensions when a user inserts an image
|
||||
///
|
||||
/// By default any image extension is allowed.
|
||||
final List<String>? 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<String>? 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<String>? 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;
|
||||
}
|
||||
53
lib/utils/plugins.dart
Normal file
53
lib/utils/plugins.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Abstract class that all the plguin classes extend
|
||||
abstract class Plugins {
|
||||
const Plugins();
|
||||
|
||||
/// Provides the JS and CSS tags to be inserted inside <head>. Only used for Web
|
||||
String getHeadString();
|
||||
|
||||
/// Provides the toolbar option for the plugin
|
||||
String getToolbarString();
|
||||
}
|
||||
|
||||
/// Summernote @ Mention plugin - adds a dropdown to select the person to mention whenever
|
||||
/// the '@' character is typed into the editor. The list of people to mention is
|
||||
/// drawn from the [getSuggestionsMobile] (on mobile) or [mentionsWeb] (on Web)
|
||||
/// parameter. You can detect who was mentioned using the [onSelect] callback.
|
||||
///
|
||||
/// README available [here](https://github.com/team-loxo/summernote-at-mention)
|
||||
class SummernoteAtMention extends Plugins {
|
||||
/// Function used to get the displayed suggestions on mobile
|
||||
final List<String> Function(String)? getSuggestionsMobile;
|
||||
|
||||
/// List of mentions to display on Web. The default behavior is to only return
|
||||
/// the mentions containing the string entered by the user in the editor
|
||||
final List<String>? mentionsWeb;
|
||||
|
||||
/// Callback to run code when a mention is selected
|
||||
final void Function(String)? onSelect;
|
||||
|
||||
const SummernoteAtMention(
|
||||
{this.getSuggestionsMobile, this.mentionsWeb, this.onSelect})
|
||||
: assert(kIsWeb ? mentionsWeb != null : getSuggestionsMobile != null);
|
||||
|
||||
@override
|
||||
String getHeadString() {
|
||||
return '<script src=\"assets/packages/html_editor_enhanced/assets/plugins/summernote-at-mention/summernote-at-mention.js\"></script>';
|
||||
}
|
||||
|
||||
@override
|
||||
String getToolbarString() {
|
||||
return '';
|
||||
}
|
||||
|
||||
String getMentionsWeb() {
|
||||
var mentionsString = '[';
|
||||
for (var e in mentionsWeb!) {
|
||||
mentionsString =
|
||||
mentionsString + "'$e'" + (e != mentionsWeb!.last ? ', ' : '');
|
||||
}
|
||||
return mentionsString + ']';
|
||||
}
|
||||
}
|
||||
10
lib/utils/shims/dart_ui.dart
Normal file
10
lib/utils/shims/dart_ui.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/// This file shims dart:ui in web-only scenarios, getting rid of the need to
|
||||
/// suppress analyzer warnings.
|
||||
|
||||
// TODO(tneotia): flutter/flutter#55000 Remove this file once web-only dart:ui APIs
|
||||
// are exposed from a dedicated place.
|
||||
export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart';
|
||||
21
lib/utils/shims/dart_ui_fake.dart
Normal file
21
lib/utils/shims/dart_ui_fake.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Fake interface for the logic that this package needs from (web-only) dart:ui.
|
||||
// This is conditionally exported so the analyzer sees these methods as available.
|
||||
|
||||
/// Shim for web_ui engine.PlatformViewRegistry
|
||||
/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62
|
||||
// ignore: camel_case_types
|
||||
class platformViewRegistry {
|
||||
/// Shim for registerViewFactory
|
||||
/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72
|
||||
static void registerViewFactory(
|
||||
String viewTypeId, dynamic Function(int viewId) viewFactory) {}
|
||||
}
|
||||
|
||||
/// Signature of callbacks that have no arguments and return no data.
|
||||
typedef VoidCallback = void Function();
|
||||
|
||||
dynamic get window => null;
|
||||
5
lib/utils/shims/dart_ui_real.dart
Normal file
5
lib/utils/shims/dart_ui_real.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright 2019 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
export 'dart:ui';
|
||||
284
lib/utils/shims/flutter_inappwebview_fake.dart
Normal file
284
lib/utils/shims/flutter_inappwebview_fake.dart
Normal file
@@ -0,0 +1,284 @@
|
||||
///Class that is used by [WebView.shouldOverrideUrlLoading] event.
|
||||
///It represents the policy to pass back to the decision handler.
|
||||
class NavigationActionPolicy {
|
||||
final int _value;
|
||||
|
||||
const NavigationActionPolicy._internal(this._value);
|
||||
|
||||
int toValue() => _value;
|
||||
|
||||
///Cancel the navigation.
|
||||
static const CANCEL = NavigationActionPolicy._internal(0);
|
||||
|
||||
///Allow the navigation to continue.
|
||||
static const ALLOW = NavigationActionPolicy._internal(1);
|
||||
|
||||
@override
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'action': _value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents the WebView context menu. It used by [WebView.contextMenu].
|
||||
///
|
||||
///**NOTE**: To make it work properly on Android, JavaScript should be enabled!
|
||||
class ContextMenu {
|
||||
///Event fired when the context menu for this WebView is being built.
|
||||
///
|
||||
///[hitTestResult] represents the hit result for hitting an HTML elements.
|
||||
final void Function(dynamic hitTestResult)? onCreateContextMenu;
|
||||
|
||||
///Event fired when the context menu for this WebView is being hidden.
|
||||
final void Function()? onHideContextMenu;
|
||||
|
||||
///Event fired when a context menu item has been clicked.
|
||||
///
|
||||
///[contextMenuItemClicked] represents the [ContextMenuItem] clicked.
|
||||
final void Function(ContextMenuItem contextMenuItemClicked)?
|
||||
onContextMenuActionItemClicked;
|
||||
|
||||
///Context menu options.
|
||||
final ContextMenuOptions? options;
|
||||
|
||||
///List of the custom [ContextMenuItem].
|
||||
final List<ContextMenuItem> menuItems;
|
||||
|
||||
ContextMenu(
|
||||
{this.menuItems = const [],
|
||||
this.onCreateContextMenu,
|
||||
this.onHideContextMenu,
|
||||
this.options,
|
||||
this.onContextMenuActionItemClicked});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'menuItems': menuItems.map((menuItem) => menuItem.toMap()).toList(),
|
||||
'options': options?.toMap()
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represent an item of the [ContextMenu].
|
||||
class ContextMenuItem {
|
||||
///Android menu item ID.
|
||||
int? androidId;
|
||||
|
||||
///iOS menu item ID.
|
||||
String? iosId;
|
||||
|
||||
///Menu item title.
|
||||
String title;
|
||||
|
||||
///Menu item action that will be called when an user clicks on it.
|
||||
Function()? action;
|
||||
|
||||
ContextMenuItem(
|
||||
{this.androidId, this.iosId, required this.title, this.action});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {'androidId': androidId, 'iosId': iosId, 'title': title};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents available options used by [ContextMenu].
|
||||
class ContextMenuOptions {
|
||||
///Whether all the default system context menu items should be hidden or not. The default value is `false`.
|
||||
bool hideDefaultSystemContextMenuItems;
|
||||
|
||||
ContextMenuOptions({this.hideDefaultSystemContextMenuItems = false});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'hideDefaultSystemContextMenuItems': hideDefaultSystemContextMenuItems
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///Class that represents contains the constants for the times at which to inject script content into a [WebView] used by an [UserScript].
|
||||
class UserScriptInjectionTime {
|
||||
final int _value;
|
||||
|
||||
const UserScriptInjectionTime._internal(this._value);
|
||||
|
||||
static final Set<UserScriptInjectionTime> values = {
|
||||
UserScriptInjectionTime.AT_DOCUMENT_START,
|
||||
UserScriptInjectionTime.AT_DOCUMENT_END,
|
||||
};
|
||||
|
||||
static UserScriptInjectionTime? fromValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return UserScriptInjectionTime.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int toValue() => _value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (_value) {
|
||||
case 1:
|
||||
return 'AT_DOCUMENT_END';
|
||||
case 0:
|
||||
default:
|
||||
return 'AT_DOCUMENT_START';
|
||||
}
|
||||
}
|
||||
|
||||
///**NOTE for iOS**: A constant to inject the script after the creation of the webpage’s document element, but before loading any other content.
|
||||
///
|
||||
///**NOTE for Android**: A constant to try to inject the script as soon as the page starts loading.
|
||||
static const AT_DOCUMENT_START = UserScriptInjectionTime._internal(0);
|
||||
|
||||
///**NOTE for iOS**: A constant to inject the script after the document finishes loading, but before loading any other subresources.
|
||||
///
|
||||
///**NOTE for Android**: A constant to inject the script as soon as the page finishes loading.
|
||||
static const AT_DOCUMENT_END = UserScriptInjectionTime._internal(1);
|
||||
|
||||
@override
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
///Class that represents a script that the [WebView] injects into the web page.
|
||||
class UserScript {
|
||||
///The script’s group name.
|
||||
String? groupName;
|
||||
|
||||
///The script’s source code.
|
||||
String source;
|
||||
|
||||
///The time at which to inject the script into the [WebView].
|
||||
UserScriptInjectionTime injectionTime;
|
||||
|
||||
///A Boolean value that indicates whether to inject the script into the main frame.
|
||||
///Specify true to inject the script only into the main frame, or false to inject it into all frames.
|
||||
///The default value is `true`.
|
||||
///
|
||||
///**NOTE**: available only on iOS.
|
||||
bool iosForMainFrameOnly;
|
||||
|
||||
///A scope of execution in which to evaluate the script to prevent conflicts between different scripts.
|
||||
///For more information about content worlds, see [ContentWorld].
|
||||
late ContentWorld contentWorld;
|
||||
|
||||
UserScript(
|
||||
{this.groupName,
|
||||
required this.source,
|
||||
required this.injectionTime,
|
||||
this.iosForMainFrameOnly = true,
|
||||
ContentWorld? contentWorld}) {
|
||||
this.contentWorld = contentWorld ?? ContentWorld.PAGE;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'groupName': groupName,
|
||||
'source': source,
|
||||
'injectionTime': injectionTime.toValue(),
|
||||
'iosForMainFrameOnly': iosForMainFrameOnly,
|
||||
'contentWorld': contentWorld.toMap()
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
final _contentWorldNameRegExp = RegExp(r'[\s]');
|
||||
|
||||
///Class that represents an object that defines a scope of execution for JavaScript code and which you use to prevent conflicts between different scripts.
|
||||
///
|
||||
///**NOTE for iOS**: available on iOS 14.0+. This class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class.
|
||||
///
|
||||
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` attribute equals to `flutter_inappwebview_[name]`
|
||||
///to the webpage's content that contains only the inline `<script>` HTML elements in order to define a new scope of execution for JavaScript code.
|
||||
///Unfortunately, there isn't any other way to do it.
|
||||
///There are some limitations:
|
||||
///- for any [ContentWorld], except [ContentWorld.PAGE] (that is the webpage itself), if you need to access to the `window` or `document` global Object,
|
||||
///you need to use `window.top` and `window.top.document` because the code runs inside an `<iframe>`;
|
||||
///- also, the execution of the inline `<script>` could be blocked by the `Content-Security-Policy` header.
|
||||
class ContentWorld {
|
||||
///The name of a custom content world.
|
||||
///It cannot contain space characters.
|
||||
final String name;
|
||||
|
||||
///Returns the custom content world with the specified name.
|
||||
ContentWorld.world({required this.name}) {
|
||||
// WINDOW-ID- is used internally by the plugin!
|
||||
assert(!name.startsWith('WINDOW-ID-') &&
|
||||
!name.contains(_contentWorldNameRegExp));
|
||||
}
|
||||
|
||||
///The default world for clients.
|
||||
// ignore: non_constant_identifier_names
|
||||
static final ContentWorld DEFAULT_CLIENT =
|
||||
ContentWorld.world(name: 'defaultClient');
|
||||
|
||||
///The content world for the current webpage’s content.
|
||||
///This property contains the content world for scripts that the current webpage executes.
|
||||
///Be careful when manipulating variables in this content world.
|
||||
///If you modify a variable with the same name as one the webpage uses, you may unintentionally disrupt the normal operation of that page.
|
||||
// ignore: non_constant_identifier_names
|
||||
static final ContentWorld PAGE = ContentWorld.world(name: 'page');
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {'name': name};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
216
lib/utils/toolbar.dart
Normal file
216
lib/utils/toolbar.dart
Normal file
@@ -0,0 +1,216 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Abstract class that all the toolbar classes extend
|
||||
abstract class Toolbar {
|
||||
const Toolbar();
|
||||
}
|
||||
|
||||
/// Style group
|
||||
class StyleButtons extends Toolbar {
|
||||
final bool style;
|
||||
|
||||
const StyleButtons({
|
||||
this.style = true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Font setting group
|
||||
class FontSettingButtons extends Toolbar {
|
||||
final bool fontName;
|
||||
final bool fontSize;
|
||||
final bool fontSizeUnit;
|
||||
|
||||
const FontSettingButtons({
|
||||
this.fontName = true,
|
||||
this.fontSize = true,
|
||||
this.fontSizeUnit = true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Font group
|
||||
class FontButtons extends Toolbar {
|
||||
final bool bold;
|
||||
final bool italic;
|
||||
final bool underline;
|
||||
final bool clearAll;
|
||||
final bool strikethrough;
|
||||
final bool superscript;
|
||||
final bool subscript;
|
||||
|
||||
const FontButtons({
|
||||
this.bold = true,
|
||||
this.italic = true,
|
||||
this.underline = true,
|
||||
this.clearAll = true,
|
||||
this.strikethrough = true,
|
||||
this.superscript = true,
|
||||
this.subscript = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons1() {
|
||||
var icons = <Icon>[];
|
||||
if (bold) icons.add(Icon(Icons.format_bold));
|
||||
if (italic) icons.add(Icon(Icons.format_italic));
|
||||
if (underline) icons.add(Icon(Icons.format_underline));
|
||||
if (clearAll) icons.add(Icon(Icons.format_clear));
|
||||
return icons;
|
||||
}
|
||||
|
||||
List<Icon> getIcons2() {
|
||||
var icons = <Icon>[];
|
||||
if (strikethrough) icons.add(Icon(Icons.format_strikethrough));
|
||||
if (superscript) icons.add(Icon(Icons.superscript));
|
||||
if (subscript) icons.add(Icon(Icons.subscript));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
|
||||
/// Color bar group
|
||||
class ColorButtons extends Toolbar {
|
||||
final bool foregroundColor;
|
||||
final bool highlightColor;
|
||||
|
||||
const ColorButtons({
|
||||
this.foregroundColor = true,
|
||||
this.highlightColor = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons() {
|
||||
var icons = <Icon>[];
|
||||
if (foregroundColor) icons.add(Icon(Icons.format_color_text));
|
||||
if (highlightColor) icons.add(Icon(Icons.format_color_fill));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
|
||||
/// List group
|
||||
class ListButtons extends Toolbar {
|
||||
final bool ul;
|
||||
final bool ol;
|
||||
final bool listStyles;
|
||||
|
||||
const ListButtons({
|
||||
this.ul = true,
|
||||
this.ol = true,
|
||||
this.listStyles = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons() {
|
||||
var icons = <Icon>[];
|
||||
if (ul) icons.add(Icon(Icons.format_list_bulleted));
|
||||
if (ol) icons.add(Icon(Icons.format_list_numbered));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
|
||||
/// Paragraph group
|
||||
class ParagraphButtons extends Toolbar {
|
||||
final bool alignLeft;
|
||||
final bool alignCenter;
|
||||
final bool alignRight;
|
||||
final bool alignJustify;
|
||||
final bool increaseIndent;
|
||||
final bool decreaseIndent;
|
||||
final bool textDirection;
|
||||
final bool lineHeight;
|
||||
final bool caseConverter;
|
||||
|
||||
const ParagraphButtons({
|
||||
this.alignLeft = true,
|
||||
this.alignCenter = true,
|
||||
this.alignRight = true,
|
||||
this.alignJustify = true,
|
||||
this.increaseIndent = true,
|
||||
this.decreaseIndent = true,
|
||||
this.textDirection = true,
|
||||
this.lineHeight = true,
|
||||
this.caseConverter = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons1() {
|
||||
var icons = <Icon>[];
|
||||
if (alignLeft) icons.add(Icon(Icons.format_align_left));
|
||||
if (alignCenter) icons.add(Icon(Icons.format_align_center));
|
||||
if (alignRight) icons.add(Icon(Icons.format_align_right));
|
||||
if (alignJustify) icons.add(Icon(Icons.format_align_justify));
|
||||
return icons;
|
||||
}
|
||||
|
||||
List<Icon> getIcons2() {
|
||||
var icons = <Icon>[];
|
||||
if (increaseIndent) icons.add(Icon(Icons.format_indent_increase));
|
||||
if (decreaseIndent) icons.add(Icon(Icons.format_indent_decrease));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert group
|
||||
class InsertButtons extends Toolbar {
|
||||
final bool link;
|
||||
final bool picture;
|
||||
final bool audio;
|
||||
final bool video;
|
||||
final bool otherFile;
|
||||
final bool table;
|
||||
final bool hr;
|
||||
|
||||
const InsertButtons({
|
||||
this.link = true,
|
||||
this.picture = true,
|
||||
this.audio = true,
|
||||
this.video = true,
|
||||
this.otherFile = false,
|
||||
this.table = true,
|
||||
this.hr = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons() {
|
||||
var icons = <Icon>[];
|
||||
if (link) icons.add(Icon(Icons.link));
|
||||
if (picture) icons.add(Icon(Icons.image_outlined));
|
||||
if (audio) icons.add(Icon(Icons.audiotrack_outlined));
|
||||
if (video) icons.add(Icon(Icons.videocam_outlined));
|
||||
if (otherFile) icons.add(Icon(Icons.attach_file));
|
||||
if (table) icons.add(Icon(Icons.table_chart_outlined));
|
||||
if (hr) icons.add(Icon(Icons.horizontal_rule));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
|
||||
/// Miscellaneous group
|
||||
class OtherButtons extends Toolbar {
|
||||
final bool fullscreen;
|
||||
final bool codeview;
|
||||
final bool undo;
|
||||
final bool redo;
|
||||
final bool help;
|
||||
final bool copy;
|
||||
final bool paste;
|
||||
|
||||
const OtherButtons({
|
||||
this.fullscreen = true,
|
||||
this.codeview = true,
|
||||
this.undo = true,
|
||||
this.redo = true,
|
||||
this.help = true,
|
||||
this.copy = true,
|
||||
this.paste = true,
|
||||
});
|
||||
|
||||
List<Icon> getIcons1() {
|
||||
var icons = <Icon>[];
|
||||
if (fullscreen) icons.add(Icon(Icons.fullscreen));
|
||||
if (codeview) icons.add(Icon(Icons.code));
|
||||
if (undo) icons.add(Icon(Icons.undo));
|
||||
if (redo) icons.add(Icon(Icons.redo));
|
||||
if (help) icons.add(Icon(Icons.help_outline));
|
||||
return icons;
|
||||
}
|
||||
|
||||
List<Icon> getIcons2() {
|
||||
var icons = <Icon>[];
|
||||
if (copy) icons.add(Icon(Icons.copy));
|
||||
if (paste) icons.add(Icon(Icons.paste));
|
||||
return icons;
|
||||
}
|
||||
}
|
||||
1201
lib/utils/utils.dart
Normal file
1201
lib/utils/utils.dart
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user