Merge pull request #8 from JideGuru/v0.0.4

chore: prepare release
This commit is contained in:
Festus Olusegun 2021-06-08 19:53:11 +01:00 committed by GitHub
commit 807d2ca2a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 199 additions and 155 deletions

View File

@ -30,7 +30,6 @@ Based on https://github.com/dankito/RichTextEditor, but for Flutter.
## 📸 Screenshots ## 📸 Screenshots
<img src="https://github.com/JideGuru/rich_editor/raw/master/res/1.png" width="400"> <img src="https://github.com/JideGuru/rich_editor/raw/master/res/1.png" width="400">
<img src="https://github.com/JideGuru/rich_editor/raw/master/res/2.png" width="400">
## Usage ## Usage

View File

@ -60,14 +60,11 @@ class BasicDemo extends StatelessWidget {
), ),
body: RichEditor( body: RichEditor(
key: keyEditor, key: keyEditor,
// value: ''' value: '''
// <h1>Heading 1</h1> Hello, This is a rich text Editor for Flutter. It supports most things like Bold, italics and underline.
// <h2>Heading 2</h2> As well as Subscript, Superscript, Colored text, Colors bg text and hyperlink.
// <h3>Heading 3</h3> Images and Videos are also supports
// <h4>Heading 4</h4> ''', // initial HTML data
// <h5>Heading 5</h5>
// <h6>Heading 6</h6>
// ''', // initial HTML data
editorOptions: RichEditorOptions( editorOptions: RichEditorOptions(
placeholder: 'Start typing', placeholder: 'Start typing',
// backgroundColor: Colors.blueGrey, // Editor's bg color // backgroundColor: Colors.blueGrey, // Editor's bg color

View File

@ -211,7 +211,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.0.3" version: "0.0.4"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -1,12 +1,10 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:rich_editor/src/extensions/extensions.dart'; import 'package:rich_editor/src/extensions/extensions.dart';
import 'package:rich_editor/src/models/callbacks/did_html_change_listener.dart'; import 'package:rich_editor/src/models/callbacks/did_html_change_listener.dart';
import 'package:rich_editor/src/models/callbacks/html_changed_listener.dart'; import 'package:rich_editor/src/models/callbacks/html_changed_listener.dart';
import 'package:rich_editor/src/models/callbacks/loaded_listener.dart'; import 'package:rich_editor/src/models/callbacks/loaded_listener.dart';
import 'package:rich_editor/src/models/editor_state.dart';
import 'package:rich_editor/src/models/enum/command_name.dart'; import 'package:rich_editor/src/models/enum/command_name.dart';
import '../models/command_state.dart'; import '../models/command_state.dart';
@ -43,10 +41,13 @@ class JavascriptExecutorBase {
List<LoadedListener> loadedListeners = <LoadedListener>[]; List<LoadedListener> loadedListeners = <LoadedListener>[];
/// Initialise the controller so we don't have to
/// pass a controller into every Method
init(InAppWebViewController? controller) { init(InAppWebViewController? controller) {
_controller = controller; _controller = controller;
} }
/// Run Javascript commands in the editor using the webview controller
executeJavascript(String command) async { executeJavascript(String command) async {
return await _controller!.evaluateJavascript(source: 'editor.$command'); return await _controller!.evaluateJavascript(source: 'editor.$command');
} }
@ -55,14 +56,16 @@ class JavascriptExecutorBase {
return htmlField!; return htmlField!;
} }
/// Display HTML data in editor
setHtml(String html) async { setHtml(String html) async {
String? baseUrl; String? baseUrl;
await executeJavascript("setHtml('" + encodeHtml(html) + "', '$baseUrl');"); await executeJavascript("setHtml('" + encodeHtml(html) + "', '$baseUrl');");
htmlField = html; htmlField = html;
} }
/// Get current HTML data from Editor
getCurrentHtml() async { getCurrentHtml() async {
String? html = await executeJavascript('getEncodedHtml()'); String? html = await executeJavascript('getEncodedHtml();');
String? decodedHtml = decodeHtml(html!); String? decodedHtml = decodeHtml(html!);
if (decodedHtml!.startsWith('"') && decodedHtml.endsWith('"')) { if (decodedHtml!.startsWith('"') && decodedHtml.endsWith('"')) {
decodedHtml = decodedHtml.substring(1, decodedHtml.length - 1); decodedHtml = decodedHtml.substring(1, decodedHtml.length - 1);
@ -70,57 +73,72 @@ class JavascriptExecutorBase {
return decodedHtml; return decodedHtml;
} }
/// Check if editor's content has been modified
bool isDefaultRichTextEditorHtml(String html) { bool isDefaultRichTextEditorHtml(String html) {
return defaultHtml == html; return defaultHtml == html;
} }
// Text commands // Text commands
/// Undo last editor command/action
undo() async { undo() async {
await executeJavascript("undo();"); await executeJavascript("undo();");
} }
/// Redo last editor command/action
redo() async { redo() async {
await executeJavascript("redo();"); await executeJavascript("redo();");
} }
/// Make selected or subsequent text Bold
setBold() async { setBold() async {
await executeJavascript("setBold();"); await executeJavascript("setBold();");
} }
/// Make selected or subsequent text Italic
setItalic() async { setItalic() async {
await executeJavascript("setItalic();"); await executeJavascript("setItalic();");
} }
/// Make selected or subsequent text Underlined
setUnderline() async { setUnderline() async {
await executeJavascript("setUnderline();"); await executeJavascript("setUnderline();");
} }
/// Make selected or subsequent text Subscript
setSubscript() async { setSubscript() async {
await executeJavascript("setSubscript();"); await executeJavascript("setSubscript();");
} }
/// Make selected or subsequent text Superscript
setSuperscript() async { setSuperscript() async {
await executeJavascript("setSuperscript();"); await executeJavascript("setSuperscript();");
} }
/// Strike through selected text
setStrikeThrough() async { setStrikeThrough() async {
await executeJavascript("setStrikeThrough();"); await executeJavascript("setStrikeThrough();");
} }
/// Set a [Color] for the selected text
setTextColor(Color? color) async { setTextColor(Color? color) async {
String? hex = color!.toHexColorString(); String? hex = color!.toHexColorString();
await executeJavascript("setTextColor('$hex');"); await executeJavascript("setTextColor('$hex');");
} }
/// Set a [Color] for the selected text's background
setTextBackgroundColor(Color? color) async { setTextBackgroundColor(Color? color) async {
String? hex = color!.toHexColorString(); String? hex = color!.toHexColorString();
await executeJavascript("setTextBackgroundColor('$hex');"); await executeJavascript("setTextBackgroundColor('$hex');");
} }
/// Apply a font face to selected text
setFontName(String fontName) async { setFontName(String fontName) async {
await executeJavascript("setFontName('$fontName');"); await executeJavascript("setFontName('$fontName');");
} }
/// Apply a font size to selected text
/// (Value can only be between 1 and 7)
setFontSize(int fontSize) async { setFontSize(int fontSize) async {
if (fontSize < 1 || fontSize > 7) { if (fontSize < 1 || fontSize > 7) {
throw ("Font size should have a value between 1-7"); throw ("Font size should have a value between 1-7");
@ -128,6 +146,8 @@ class JavascriptExecutorBase {
await executeJavascript("setFontSize('$fontSize');"); await executeJavascript("setFontSize('$fontSize');");
} }
/// Apply a Heading style to selected text
/// (Value can only be between 1 and 6)
setHeading(int heading) async { setHeading(int heading) async {
await executeJavascript("setHeading('$heading');"); await executeJavascript("setHeading('$heading');");
} }
@ -140,47 +160,58 @@ class JavascriptExecutorBase {
await executeJavascript("setPreformat();"); await executeJavascript("setPreformat();");
} }
/// Create BlockQuote / make selected text a BlockQuote
setBlockQuote() async { setBlockQuote() async {
await executeJavascript("setBlockQuote();"); await executeJavascript("setBlockQuote();");
} }
/// Remove formatting from selected text
removeFormat() async { removeFormat() async {
await executeJavascript("removeFormat();"); await executeJavascript("removeFormat();");
} }
/// Align content left
setJustifyLeft() async { setJustifyLeft() async {
await executeJavascript("setJustifyLeft();"); await executeJavascript("setJustifyLeft();");
} }
/// Align content center
setJustifyCenter() async { setJustifyCenter() async {
await executeJavascript("setJustifyCenter();"); await executeJavascript("setJustifyCenter();");
} }
/// Align content right
setJustifyRight() async { setJustifyRight() async {
await executeJavascript("setJustifyRight();"); await executeJavascript("setJustifyRight();");
} }
/// Justify content
setJustifyFull() async { setJustifyFull() async {
await executeJavascript("setJustifyFull();"); await executeJavascript("setJustifyFull();");
} }
/// Add indentation
setIndent() async { setIndent() async {
await executeJavascript("setIndent();"); await executeJavascript("setIndent();");
} }
/// Remove indentation
setOutdent() async { setOutdent() async {
await executeJavascript("setOutdent();"); await executeJavascript("setOutdent();");
} }
/// Start an unordered list
insertBulletList() async { insertBulletList() async {
await executeJavascript("insertBulletList();"); await executeJavascript("insertBulletList();");
} }
/// Start a ordered list
insertNumberedList() async { insertNumberedList() async {
await executeJavascript("insertNumberedList();"); await executeJavascript("insertNumberedList();");
} }
// Insert element // Insert element
/// Insert hyper link / make selected text an hyperlink
insertLink(String url, String title) async { insertLink(String url, String title) async {
await executeJavascript("insertLink('$url', '$title');"); await executeJavascript("insertLink('$url', '$title');");
} }
@ -217,50 +248,63 @@ class JavascriptExecutorBase {
); );
} }
/// Add a checkbox to the current editor
insertCheckbox(String text) async { insertCheckbox(String text) async {
await executeJavascript("insertCheckbox('$text');"); await executeJavascript("insertCheckbox('$text');");
} }
/// Insert HTML code into the editor
/// (It wont display the HTML code but it'll render it)
insertHtml(String html) async { insertHtml(String html) async {
String? encodedHtml = encodeHtml(html); String? encodedHtml = encodeHtml(html);
await executeJavascript("insertHtml('$encodedHtml');"); await executeJavascript("insertHtml('$encodedHtml');");
} }
/// Enable Images resizing
makeImagesResizeable() async { makeImagesResizeable() async {
await executeJavascript("makeImagesResizeable();"); await executeJavascript("makeImagesResizeable();");
} }
/// Disable Images resizing
disableImageResizing() async { disableImageResizing() async {
await executeJavascript("disableImageResizing();"); await executeJavascript("disableImageResizing();");
} }
// Editor settings commands // Editor settings commands
/// Focus on editor and bring up keyboard
focus() async { focus() async {
await executeJavascript("focus();"); await executeJavascript("focus();");
SystemChannels.textInput.invokeMethod('TextInput.show');
} }
/// Remove focus from the editor and close the keyboard
unFocus() async { unFocus() async {
await executeJavascript("blurFocus();"); await executeJavascript("blurFocus();");
} }
/// Set a [Color] for the editor's background
setBackgroundColor(Color? color) async { setBackgroundColor(Color? color) async {
String? hex = color!.toHexColorString(); String? hex = color!.toHexColorString();
await executeJavascript("setBackgroundColor('$hex');"); await executeJavascript("setBackgroundColor('$hex');");
} }
/// Set an image for the editor's background
setBackgroundImage(String image) async { setBackgroundImage(String image) async {
await executeJavascript("setBackgroundImage('$image');"); await executeJavascript("setBackgroundImage('$image');");
} }
/// Set a default editor text color
setBaseTextColor(Color? color) async { setBaseTextColor(Color? color) async {
String? hex = color!.toHexColorString(); String? hex = color!.toHexColorString();
await executeJavascript("setBaseTextColor('$hex');"); await executeJavascript("setBaseTextColor('$hex');");
} }
/// Set a default editor text font family
setBaseFontFamily(String fontFamily) async { setBaseFontFamily(String fontFamily) async {
await executeJavascript("setBaseFontFamily('$fontFamily');"); await executeJavascript("setBaseFontFamily('$fontFamily');");
} }
/// Add padding to the editor's content
setPadding(EdgeInsets? padding) async { setPadding(EdgeInsets? padding) async {
String left = padding!.left.toString(); String left = padding!.left.toString();
String top = padding.top.toString(); String top = padding.top.toString();
@ -270,19 +314,23 @@ class JavascriptExecutorBase {
"setPadding('${left}px', '${top}px', '${right}px', '${bottom}px');"); "setPadding('${left}px', '${top}px', '${right}px', '${bottom}px');");
} }
// Doesnt actually work for' now /// Set a hint when the editor is empty
/// Doesn't actually work for now
setPlaceholder(String placeholder) async { setPlaceholder(String placeholder) async {
await executeJavascript("setPlaceholder('$placeholder');"); await executeJavascript("setPlaceholder('$placeholder');");
} }
/// Set editor's width in pixels
setEditorWidth(int px) async { setEditorWidth(int px) async {
await executeJavascript("setWidth('" + px.toString() + "px');"); await executeJavascript("setWidth('" + px.toString() + "px');");
} }
/// Set editor's height in pixels
setEditorHeight(int px) async { setEditorHeight(int px) async {
await executeJavascript("setHeight('" + px.toString() + "px');"); await executeJavascript("setHeight('" + px.toString() + "px');");
} }
/// Enable text input on editor
setInputEnabled(bool inputEnabled) async { setInputEnabled(bool inputEnabled) async {
await executeJavascript("setInputEnabled($inputEnabled);"); await executeJavascript("setInputEnabled($inputEnabled);");
} }
@ -295,143 +343,143 @@ class JavascriptExecutorBase {
return Uri.encodeFull(html); return Uri.encodeFull(html);
} }
bool shouldOverrideUrlLoading(String url) { // bool shouldOverrideUrlLoading(String url) {
String decodedUrl; // String decodedUrl;
try { // try {
decodedUrl = decodeHtml(url); // decodedUrl = decodeHtml(url);
} catch (e) { // } catch (e) {
// No handling // // No handling
return false; // return false;
} // }
//
if (url.indexOf(editorStateChangedCallbackScheme) == 0) { // if (url.indexOf(editorStateChangedCallbackScheme) == 0) {
editorStateChanged( // editorStateChanged(
decodedUrl.substring(editorStateChangedCallbackScheme.length)); // decodedUrl.substring(editorStateChangedCallbackScheme.length));
return true; // return true;
} // }
//
return false; // return false;
} // }
//
editorStateChanged(String statesString) { // editorStateChanged(String statesString) {
try { // try {
var editorState = EditorState.fromJson(jsonDecode(statesString)); // var editorState = EditorState.fromJson(jsonDecode(statesString));
//
bool currentHtmlChanged = this.htmlField != editorState.html; // bool currentHtmlChanged = this.htmlField != editorState.html;
this.htmlField = editorState.html; // this.htmlField = editorState.html;
//
retrievedEditorState( // retrievedEditorState(
editorState.didHtmlChange!, editorState.commandStates!); // editorState.didHtmlChange!, editorState.commandStates!);
//
if (currentHtmlChanged) { // if (currentHtmlChanged) {
// fireHtmlChangedListenersAsync(editorState.html); // // fireHtmlChangedListenersAsync(editorState.html);
} // }
} catch (e) { // } catch (e) {
throw ("Could not parse command states: $statesString $e"); // throw ("Could not parse command states: $statesString $e");
} // }
} // }
//
retrievedEditorState( // retrievedEditorState(
bool didHtmlChange, Map<CommandName, CommandState> commandStates) { // bool didHtmlChange, Map<CommandName, CommandState> commandStates) {
if (this.didHtmlChange != didHtmlChange) { // if (this.didHtmlChange != didHtmlChange) {
this.didHtmlChange = didHtmlChange; // this.didHtmlChange = didHtmlChange;
didHtmlChangeListeners.forEach((element) { // didHtmlChangeListeners.forEach((element) {
element.didHtmlChange(didHtmlChange); // element.didHtmlChange(didHtmlChange);
}); // });
} // }
//
handleRetrievedCommandStates(commandStates); // handleRetrievedCommandStates(commandStates);
} // }
//
handleRetrievedCommandStates(Map<CommandName, CommandState> commandStates) { // handleRetrievedCommandStates(Map<CommandName, CommandState> commandStates) {
determineDerivedCommandStates(commandStates); // determineDerivedCommandStates(commandStates);
//
this.commandStates = commandStates; // this.commandStates = commandStates;
commandStatesChangedListeners.forEach((element) { // commandStatesChangedListeners.forEach((element) {
element = this.commandStates; // element = this.commandStates;
}); // });
} // }
//
determineDerivedCommandStates(Map<CommandName, CommandState> commandStates) { // determineDerivedCommandStates(Map<CommandName, CommandState> commandStates) {
if (commandStates[CommandName.FORMATBLOCK] != null) { // if (commandStates[CommandName.FORMATBLOCK] != null) {
var formatCommandState = commandStates[CommandName.FORMATBLOCK]; // var formatCommandState = commandStates[CommandName.FORMATBLOCK];
commandStates.update( // commandStates.update(
CommandName.H1, // CommandName.H1,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h1")), // isFormatActivated(formatCommandState, "h1")),
); // );
commandStates.update( // commandStates.update(
CommandName.H2, // CommandName.H2,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h2"))); // isFormatActivated(formatCommandState, "h2")));
commandStates.update( // commandStates.update(
CommandName.H3, // CommandName.H3,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h3")), // isFormatActivated(formatCommandState, "h3")),
); // );
commandStates.update( // commandStates.update(
CommandName.H4, // CommandName.H4,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h4")), // isFormatActivated(formatCommandState, "h4")),
); // );
commandStates.update( // commandStates.update(
CommandName.H5, // CommandName.H5,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h5")), // isFormatActivated(formatCommandState, "h5")),
); // );
commandStates.update( // commandStates.update(
CommandName.H6, // CommandName.H6,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h6")), // isFormatActivated(formatCommandState, "h6")),
); // );
commandStates.update( // commandStates.update(
CommandName.P, // CommandName.P,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "p")), // isFormatActivated(formatCommandState, "p")),
); // );
commandStates.update( // commandStates.update(
CommandName.PRE, // CommandName.PRE,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "pre")), // isFormatActivated(formatCommandState, "pre")),
); // );
commandStates.update( // commandStates.update(
CommandName.BR, // CommandName.BR,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "")), // isFormatActivated(formatCommandState, "")),
); // );
commandStates.update( // commandStates.update(
CommandName.BLOCKQUOTE, // CommandName.BLOCKQUOTE,
(val) => CommandState(formatCommandState!.executable, // (val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "blockquote")), // isFormatActivated(formatCommandState, "blockquote")),
); // );
} // }
//
if (commandStates[CommandName.INSERTHTML] != null) { // if (commandStates[CommandName.INSERTHTML] != null) {
CommandState? insertHtmlState = commandStates[CommandName.INSERTHTML]; // CommandState? insertHtmlState = commandStates[CommandName.INSERTHTML];
commandStates.update(CommandName.INSERTLINK, (val) => insertHtmlState!); // commandStates.update(CommandName.INSERTLINK, (val) => insertHtmlState!);
commandStates.update(CommandName.INSERTIMAGE, (val) => insertHtmlState!); // commandStates.update(CommandName.INSERTIMAGE, (val) => insertHtmlState!);
commandStates.update( // commandStates.update(
CommandName.INSERTCHECKBOX, (val) => insertHtmlState!); // CommandName.INSERTCHECKBOX, (val) => insertHtmlState!);
} // }
} // }
//
String isFormatActivated(CommandState formatCommandState, String format) { // String isFormatActivated(CommandState formatCommandState, String format) {
return (formatCommandState.value == format) // return (formatCommandState.value == format)
.toString(); // rich_text_editor.js reports boolean values as string, so we also have to convert it to string // .toString(); // rich_text_editor.js reports boolean values as string, so we also have to convert it to string
} // }
//
addCommandStatesChangedListener( // addCommandStatesChangedListener(
Map<CommandName, CommandState> commandStates) { // Map<CommandName, CommandState> commandStates) {
commandStatesChangedListeners.add(commandStates); // commandStatesChangedListeners.add(commandStates);
//
// listener.invoke(commandStates); // // listener.invoke(commandStates);
} // }
//
addDidHtmlChangeListener(DidHtmlChangeListener listener) { // addDidHtmlChangeListener(DidHtmlChangeListener listener) {
didHtmlChangeListeners.add(listener); // didHtmlChangeListeners.add(listener);
} // }
//
addHtmlChangedListener(HtmlChangedListener listener) { // addHtmlChangedListener(HtmlChangedListener listener) {
htmlChangedListeners.add(listener); // htmlChangedListeners.add(listener);
} // }
} }

View File

@ -1,6 +1,6 @@
name: rich_editor name: rich_editor
description: WYSIWYG editor for Flutter with a rich set of supported formatting options. description: WYSIWYG editor for Flutter with a rich set of supported formatting options.
version: 0.0.3 version: 0.0.4
homepage: https://github.com/JideGuru/rich_editor homepage: https://github.com/JideGuru/rich_editor
environment: environment:

BIN
res/1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
res/2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB