diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c70d58..32fa25fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [1.2.0] - 2021-02-20 +* Add support for accessing `InAppWebViewController` via a getter +* Add support for inserting files via the editor dialog itself +* Add methods: + * toggle code view + * enable/disable editor + * undo/redo + * inserting plaintext/HTML/images/links +* Add callbacks: + * onChange + * onEnter + * onFocus/onBlur/onBlurCodeview + * onKeyUp/onKeyDown + * onPaste +* Downgraded dependencies to non-nullsafety to prevent errors +* Updated docs and example app to showcase new features, refer to those for info on the above changes + ## [1.1.1] - 2021-02-19 * Minor update to add documentation to code and completely refactor/reorganize code diff --git a/README.md b/README.md index d67d4c14..2310b884 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,62 @@ # Flutter Html Editor - Enhanced +[![pub package](https://img.shields.io/pub/v/html_editor_enhanced.svg)](https://pub.dev/packages/html_editor_enhanced) -Flutter HTML Editor is a text editor for Android and iOS to help write WYSIWYG HTML code with on the Summernote JavaScript wrapper. +Flutter HTML Editor Enhanced is a text editor for Android and iOS to help write WYSIWYG HTML code with on the Summernote JavaScript wrapper. -![demo example](https://github.com/xrb21/flutter-html-editor/blob/master/screenshoot/flutter_html_editor.gif) ![demo android](https://github.com/xrb21/flutter-html-editor/blob/master/screenshoot/sc.jpeg) ![demo ios](https://github.com/xrb21/flutter-html-editor/blob/master/screenshoot/sc_iphone.png) + + + + + + + +
Video Example
GIF example
+ +## Table of Contents: + +- ["Enhanced"? In what ways?](#in-what-ways-is-this-package-enhanced) + +- [Setup](#setup) + +- [Usage](#basic-usage) + +- [API Reference](#api-reference) + + - [Parameters Table](#parameters) + + - [Methods Table](#methods) + + - [Callbacks Table](#callbacks) + + - [Getters](#getters) + + - [Examples](#examples) + +- [Notes](#notes) + +- [License](#license) + +- [Contribution Guide](#contribution-guide) + +## In what ways is this package "enhanced"? + +1. It uses a heavily optimized [WebView](https://github.com/pichillilorenzo/flutter_inappwebview) to deliver the best possible experience when using the editor + +2. It doesn't use a local server to load the HTML code containing the editor. Instead, this package simply loads the HTML file, which improves performance and the editor's startup time. + +3. It uses a `StatelessWidget`. You don't have to fiddle around with `GlobalKey`s to access methods, instead you can simply call `HtmlEditor.` anywhere you want. + +4. It has support for many of Summernote's methods + +5. It has support for many of Summernote's callbacks + +6. It exposes the `InAppWebViewController` so you can customize the WebView however you like - you can even load your own HTML code and inject your own JavaScript for your use cases. + +More is on the way! File a feature request or contribute to the project if you'd like to see other features added. ## Setup -Add `html_editor_enhanced: ^1.1.1` as dependency to your pubspec.yaml +Add `html_editor_enhanced: ^1.2.0` as dependency to your pubspec.yaml
Follow the setup instructions for the image_picker plugin @@ -21,6 +71,7 @@ Add the following keys to your _Info.plist_ file, located in `/ios ### Android #### API < 29 + No configuration required - the plugin should work out of the box. #### API 29+ @@ -29,7 +80,11 @@ Add `android:requestLegacyExternalStorage="true"` as an attribute to the ` -Additional setup is required to allow the user to pick images via ``, add the following to your app's AndroidManifest.xml inside the `` tag: +Additional setup is required to allow the user to pick images via ``: + +
Instructions + +Add the following to your app's AndroidManifest.xml inside the `` tag: ```xml + +``` + +In Dart, you'll need to request these permissions. You can use [`permission_handler`](https://pub.dev/packages/permission_handler) like this: + +```dart +//Android +await Permission.storage.request(); +//iOS +await Permission.photos.request(); +``` + +If you'd like the user to be able to insert images via the camera, you need to request for those permissions. AndroidManifest: + +```xml + +``` + +Dart: + +```dart +await Permission.camera.request(); +``` + +You must request the permissions in Dart before the user accesses the file upload dialog. I recommend requesting the permissions in `initState()` or something similar. + +IMPORTANT: When using `permission_handler` on iOS, you must modify the Podfile, otherwise you will not be able to upload a build to App Store Connect. Add the following at the very bottom, right underneath `flutter_additional_ios_build_settings(target)`: + +```text +target.build_configurations.each do |config| + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + '$(inherited)', + ## use a hashtag symbol on the permissions you want to include in your app. Make sure they are defined in Info.plist as well! + ## dart: PermissionGroup.calendar + 'PERMISSION_EVENTS=0', + + ## dart: PermissionGroup.reminders + 'PERMISSION_REMINDERS=0', + + ## dart: PermissionGroup.contacts + 'PERMISSION_CONTACTS=0', + + ## dart: PermissionGroup.camera + 'PERMISSION_CAMERA=0', + + ## dart: PermissionGroup.microphone + 'PERMISSION_MICROPHONE=0', + + ## dart: PermissionGroup.speech + 'PERMISSION_SPEECH_RECOGNIZER=0', + + ## dart: PermissionGroup.photos + # 'PERMISSION_PHOTOS=0', + + ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] + 'PERMISSION_LOCATION=0', + + ## dart: PermissionGroup.notification + 'PERMISSION_NOTIFICATIONS=0', + + ## dart: PermissionGroup.mediaLibrary + 'PERMISSION_MEDIA_LIBRARY=0', + + ## dart: PermissionGroup.sensors + 'PERMISSION_SENSORS=0' + ] + end +``` + +If you decide to allow images directly from the camera, you will need to comment `'PERMISSION_CAMERA=0',` as well. + +
+ +## Basic Usage ```dart import 'package:html_editor/html_editor.dart'; @@ -62,35 +194,88 @@ When you want to get text from the editor: final txt = await HtmlEditor.getText(); ``` -### In what ways is this package "enhanced"? +## API Reference -1. It uses a heavily optimized [WebView](https://github.com/pichillilorenzo/flutter_inappwebview) to deliver the best possible experience when using the editor +For the full API reference, see [here](https://pub.dev/documentation/html_editor_enhanced/latest/). -2. It doesn't use a local server to load the HTML code containing the editor. Instead, this package simply loads the HTML file, which improves performance and the editor's startup time. - -3. It uses a `StatelessWidget`. You don't have to fiddle around with `GlobalKey`s to access methods, instead you can simply call `HtmlEditor.` anywhere you want. +For a full example, see [here](https://github.com/tneotia/html-editor-enhanced/tree/master/example). -4. It has support for many of Summernote's methods +Below, you will find brief descriptions of the parameters the`HtmlEditor` widget accepts and some code snippets to help you use this package. -5. It has support for many of Summernote's callbacks +### Parameters -6. It exposes the `InAppWebViewController` so you can customize the WebView however you like - you can even load your own HTML code and inject your own JavaScript for your use cases. +Parameter | Type | Default | Description +------------ | ------------- | ------------- | ------------- +**initialText** | `String` | empty | Initial text content for text editor +**height** | `double` | 380 | Height of text editor (does not set the height in HTML yet, only the height of the WebView widget) +**decoration** | `BoxDecoration` | | `BoxDecoration` that surrounds the widget +**useBottomSheet** | `bool` | true | If true, open a bottom sheet (Android intent style) to pick an image, otherwise use a dialog +**imageWidth** | `double` | 100 | Width of image once inserted. Must be between 0 and 100. +**showBottomToolbar** | `bool` | true | Show or hide bottom toolbar +**hint** | `String` | empty | Placeholder hint text +**callbacks** | `Callbacks` | empty | Customize the callbacks for various events -More is on the way! File a feature request or contribute to the project if you'd like to see other features added. +### Methods -### Parameters +Access these methods like this: `HtmlEditor.` -Parameter | Type | Default | Description +Method | Argument(s) | Returned Value(s) | Description ------------ | ------------- | ------------- | ------------- -**value** | String | empty | initial text content for text editor -**height** | double | 380 | height of text editor -**decoration** | BoxDecoration | | Decoration editor -**useBottomSheet** | bool | true | if true, open a bottom sheet (Android intent style) to pick an image, otherwise use a dialog -**widthImage** | String | 100% | width of image picker -**showBottomToolbar** | bool | true | show or hide bottom toolbar -**hint** | String | empty | Placeholder hint text -**callbacks** | Callbacks | empty | Customize the callbacks for various events +**getText()** | N/A | `Future` | Returns the current HTML in the editor +**setText()** | `String` | N/A | Sets the current text in the HTML to the input HTML string +**setFullScreen()** | N/A | N/A | Sets the editor to take up the entire size of the webview +**setFocus()** | N/A | N/A | If the pointer is in the webview, the focus will be set to the editor box +**clear()** | N/A | N/A | Resets the HTML editor to its default state +**setHint()** | `String` | N/A | Sets the current hint text of the editor +**toggleCodeview()** | N/A | N/A | Toggles between the code view and the rich text view +**disable()** | N/A | N/A | Disables the editor (a gray mask is applied and all touches are absorbed) +**enable()** | N/A | N/A | Enables the editor +**undo()** | N/A | N/A | Undoes the last command in the editor +**redo()** | N/A | N/A | Redoes the last command in the editor +**insertText()** | `String` | N/A | Inserts the provided text into the editor at the current cursor position. Do *not* use this method for HTML strings. +**insertHtml()** | `String` | N/A | Inserts the provided HTML string into the editor at the current cursor position. Do *not* use this method for plaintext strings. +**insertNetworkImage()** | `String` url, `String` filename (optional) | N/A | Inserts an image using the provided url and optional filename into the editor at the current cursor position. The image must be accessible via a URL. +**insertLink()** | `String` text, `String` url, `bool` isNewWindow | N/A | Inserts a hyperlink using the provided text and url into the editor at the current cursor position. `isNewWindow` defines whether a new browser window is launched if the link is tapped. + +### Callbacks + +Every callback is defined as a `Function()`. See the [documentation](https://pub.dev/documentation/html_editor_enhanced/latest/) for more specific details on each callback. + +Callback | Parameter(s) | Description +------------ | ------------- | ------------- +**onChange** | `String` | Called when the content of the editor changes, passes the current HTML in the editor +**onEnter** | N/A | Called when enter/return is pressed +**onFocus** | N/A | Called when the rich text field gains focus +**onBlur** | N/A | Called when the rich text field or the codeview loses focus +**onBlurCodeview** | N/A | Called when the codeview either gains or loses focus +**onKeyDown** | `int` | Called when a key is downed, passes the keycode of the downed key +**onKeyUp** | `int` | Called when a key is released, passes the keycode of the released key +**onPaste** | N/A | Called when content is pasted into the editor + +### Getters + +Currently, the package has one getter: `HtmlEditor.controller`. This returns the `InAppWebViewController`, which manages the webview that displays the editor. + +This is extremely powerful, as it allows you to create your own custom methods and implementations directly in your app. See [`flutter_inappwebview`](https://github.com/pichillilorenzo/flutter_inappwebview) for documentation on the controller. + +### Examples + +See the [example app](https://github.com/tneotia/html-editor-enhanced/blob/master/example/lib/main.dart) to see how the majority of methods & callbacks can be used. You can also play around with the parameters to see how they function. + +This section will be updated later with more specialized and specific examples as this library grows and more features are implemented. + +## Notes + +Due to this package depending on a webview for rendering the HTML editor, there will be some general weirdness in how the editor behaves. Unfortunately, these are not things I can fix, they are inherent problems with how webviews function on Flutter. + +If you do find any issues, please report them in the Issues tab and I will see if a fix is possible, but if I close the issue it is likely due to the above fact. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Contribution Guide + +> Coming soon! +> +> Meanwhile, PRs are always welcome \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 78e8f6ec..669a566f 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,42 +7,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.3" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.5" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.15.0" convert: dependency: transitive description: @@ -70,7 +70,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -89,7 +89,7 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-nullsafety.2" + version: "1.0.11" flutter_test: dependency: "direct dev" description: flutter @@ -108,42 +108,42 @@ packages: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.0-nullsafety.0" + version: "0.12.2" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.0-nullsafety" + version: "3.1.4" image_picker: dependency: transitive description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.7.0-nullsafety" + version: "0.6.7+22" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-nullsafety" + version: "1.1.6" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.3" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.3.0" mime: dependency: transitive description: @@ -157,7 +157,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.3" + version: "1.8.0" pedantic: dependency: transitive description: @@ -183,49 +183,49 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.4" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.6" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.6" + version: "0.2.19" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.5" + version: "1.3.0" uuid: dependency: transitive description: @@ -239,7 +239,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.5" + version: "2.1.0" sdks: dart: ">=2.12.0-0.0 <3.0.0" flutter: ">=1.20.0" diff --git a/pubspec.lock b/pubspec.lock index f29c14de..ffae6d1e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" characters: dependency: transitive description: @@ -15,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" collection: dependency: transitive description: @@ -36,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -54,35 +82,47 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-nullsafety.2" + version: "1.0.11" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" http: dependency: transitive description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.13.0" + version: "0.12.2" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "3.1.4" image_picker: dependency: "direct main" description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.7.0-nullsafety" + version: "0.6.7+22" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0-nullsafety" + version: "1.1.6" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" meta: dependency: transitive description: @@ -91,7 +131,7 @@ packages: source: hosted version: "1.3.0" mime: - dependency: "direct main" + dependency: transitive description: name: mime url: "https://pub.dartlang.org" @@ -129,7 +169,21 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -144,6 +198,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d24ebff9..22f02cdd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: html_editor_enhanced description: HTML Editor Enhanced is a text editor for Android and iOS to help write WYSIWYG HTML code using Summernote. Initially created by @xrb21, enhanced version maintained by @tneotia. -version: 1.1.1 +version: 1.2.0 homepage: https://github.com/tneotia/html-editor-enhanced environment: