feat: working on editor state

This commit is contained in:
jideguru 2021-06-06 16:42:55 +01:00
parent 3e8ade5b18
commit 066b2019f2
9 changed files with 138 additions and 55 deletions

View File

@ -10,20 +10,11 @@ PODS:
- image_picker (0.0.1): - image_picker (0.0.1):
- Flutter - Flutter
- OrderedSet (5.0.0) - OrderedSet (5.0.0)
- video_player (0.0.1):
- Flutter
- wakelock (0.0.1):
- Flutter
- webview_flutter (0.0.1):
- Flutter
DEPENDENCIES: DEPENDENCIES:
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
- webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
@ -36,21 +27,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_inappwebview/ios" :path: ".symlinks/plugins/flutter_inappwebview/ios"
image_picker: image_picker:
:path: ".symlinks/plugins/image_picker/ios" :path: ".symlinks/plugins/image_picker/ios"
video_player:
:path: ".symlinks/plugins/video_player/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
webview_flutter:
:path: ".symlinks/plugins/webview_flutter/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
webview_flutter: 9f491a9b5a66f2573946a389b2677987b0ff8c0b
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c

View File

@ -0,0 +1,3 @@
class DidHtmlChangeListener {
didHtmlChange(bool didHtmlChange) {}
}

View File

@ -0,0 +1,3 @@
class GetCurrentHtmlCallback {
htmlRetrieved(String html) {}
}

View File

@ -0,0 +1,3 @@
class HtmlChangedListener {
htmlChangedAsync(String html) {}
}

View File

@ -0,0 +1,3 @@
class LoadedListener {
editorLoaded() {}
}

View File

@ -9,6 +9,7 @@ class RichEditorOptions {
String? placeholder; String? placeholder;
String? baseFontFamily; String? baseFontFamily;
BarPosition? barPosition; BarPosition? barPosition;
bool? enableVideo;
RichEditorOptions({ RichEditorOptions({
Color? backgroundColor, Color? backgroundColor,
@ -17,6 +18,7 @@ class RichEditorOptions {
String? placeholder, String? placeholder,
String? baseFontFamily, String? baseFontFamily,
BarPosition? barPosition, BarPosition? barPosition,
bool? enableVideo = true,
}) { }) {
this.backgroundColor = backgroundColor; this.backgroundColor = backgroundColor;
this.baseTextColor = baseTextColor; this.baseTextColor = baseTextColor;
@ -24,5 +26,6 @@ class RichEditorOptions {
this.placeholder = placeholder; this.placeholder = placeholder;
this.baseFontFamily = baseFontFamily; this.baseFontFamily = baseFontFamily;
this.barPosition = barPosition; this.barPosition = barPosition;
this.enableVideo = enableVideo;
} }
} }

View File

@ -10,7 +10,6 @@ import 'package:rich_editor/src/models/rich_editor_options.dart';
import 'package:rich_editor/src/services/local_server.dart'; import 'package:rich_editor/src/services/local_server.dart';
import 'package:rich_editor/src/utils/javascript_executor_base.dart'; import 'package:rich_editor/src/utils/javascript_executor_base.dart';
import 'package:rich_editor/src/widgets/editor_tool_bar.dart'; import 'package:rich_editor/src/widgets/editor_tool_bar.dart';
// import 'package:webview_flutter/webview_flutter.dart';
class RichEditor extends StatefulWidget { class RichEditor extends StatefulWidget {
final String? value; final String? value;
@ -35,8 +34,6 @@ class RichEditorState extends State<RichEditor> {
final Key _mapKey = UniqueKey(); final Key _mapKey = UniqueKey();
String assetPath = 'packages/rich_editor/assets/editor/editor.html'; String assetPath = 'packages/rich_editor/assets/editor/editor.html';
// InAppWebViewController? webViewController;
int port = 5321; int port = 5321;
String html = ''; String html = '';
LocalServer? localServer; LocalServer? localServer;
@ -94,6 +91,7 @@ class RichEditorState extends State<RichEditor> {
child: EditorToolBar( child: EditorToolBar(
getImageUrl: widget.getImageUrl, getImageUrl: widget.getImageUrl,
javascriptExecutor: javascriptExecutor, javascriptExecutor: javascriptExecutor,
enableVideo: widget.editorOptions!.enableVideo,
), ),
), ),
Expanded( Expanded(
@ -114,9 +112,11 @@ class RichEditorState extends State<RichEditor> {
} }
}, },
onLoadStop: (controller, link) async { onLoadStop: (controller, link) async {
javascriptExecutor.init(_controller!); if (link!.path != 'blank') {
await _setInitialValues(); javascriptExecutor.init(_controller!);
_addJSListener(); await _setInitialValues();
_addJSListener();
}
}, },
// javascriptMode: JavascriptMode.unrestricted, // javascriptMode: JavascriptMode.unrestricted,
// gestureNavigationEnabled: false, // gestureNavigationEnabled: false,
@ -138,6 +138,7 @@ class RichEditorState extends State<RichEditor> {
child: EditorToolBar( child: EditorToolBar(
getImageUrl: widget.getImageUrl, getImageUrl: widget.getImageUrl,
javascriptExecutor: javascriptExecutor, javascriptExecutor: javascriptExecutor,
enableVideo: widget.editorOptions!.enableVideo,
), ),
), ),
], ],

View File

@ -3,6 +3,9 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/html_changed_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/editor_state.dart';
import 'package:rich_editor/src/models/enum/command_name.dart'; import 'package:rich_editor/src/models/enum/command_name.dart';
@ -19,9 +22,27 @@ class JavascriptExecutorBase {
String defaultEncoding = "UTF-8"; String defaultEncoding = "UTF-8";
String? htmlField = ""; String? htmlField = "";
var didHtmlChange = false; var didHtmlChange = false;
Map<CommandName, CommandState> commandStates = {}; Map<CommandName, CommandState> commandStates = {};
List<Map<CommandName, CommandState>> commandStatesChangedListeners =
<Map<CommandName, CommandState>>[];
List<DidHtmlChangeListener> didHtmlChangeListeners =
<DidHtmlChangeListener>[];
List<HtmlChangedListener> htmlChangedListeners = <HtmlChangedListener>[];
// protected val fireHtmlChangedListenersQueue = AsyncProducerConsumerQueue<String>(1) { html ->
// fireHtmlChangedListeners(html)
// }
bool isLoaded = false;
List<LoadedListener> loadedListeners = <LoadedListener>[];
init(InAppWebViewController? controller) { init(InAppWebViewController? controller) {
_controller = controller; _controller = controller;
} }
@ -42,8 +63,8 @@ class JavascriptExecutorBase {
getCurrentHtml() async { getCurrentHtml() async {
String? html = await executeJavascript('getEncodedHtml()'); String? html = await executeJavascript('getEncodedHtml()');
String? decodedHtml = Uri.decodeFull(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);
} }
return decodedHtml; return decodedHtml;
@ -247,11 +268,11 @@ class JavascriptExecutorBase {
await executeJavascript("setInputEnabled($inputEnabled);"); await executeJavascript("setInputEnabled($inputEnabled);");
} }
static decodeHtml(String html) { decodeHtml(String html) {
return Uri.decodeFull(html); return Uri.decodeFull(html);
} }
static encodeHtml(String html) { encodeHtml(String html) {
return Uri.encodeFull(html); return Uri.encodeFull(html);
} }
@ -295,9 +316,9 @@ class JavascriptExecutorBase {
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 { didHtmlChangeListeners.forEach((element) {
// it.didHtmlChange(didHtmlChange); element.didHtmlChange(didHtmlChange);
// } });
} }
handleRetrievedCommandStates(commandStates); handleRetrievedCommandStates(commandStates);
@ -307,37 +328,91 @@ class JavascriptExecutorBase {
determineDerivedCommandStates(commandStates); determineDerivedCommandStates(commandStates);
this.commandStates = commandStates; this.commandStates = commandStates;
commandStatesChangedListeners.forEach((element) {
// commandStatesChangedListeners.forEach { element = this.commandStates;
// it.invoke(this.commandStates) });
// }
} }
determineDerivedCommandStates(Map<CommandName, CommandState> commandStates) { determineDerivedCommandStates(Map<CommandName, CommandState> commandStates) {
// commandStates[CommandName.FORMATBLOCK]?.let { if (commandStates[CommandName.FORMATBLOCK] != null) {
// formatCommandState var formatCommandState = commandStates[CommandName.FORMATBLOCK];
// -> commandStates.update(
// commandStates.put(CommandName.H1, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h1"))) CommandName.H1,
// commandStates.put(CommandName.H2, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h2"))) (val) => CommandState(formatCommandState!.executable,
// commandStates.put(CommandName.H3, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h3"))) isFormatActivated(formatCommandState, "h1")),
// commandStates.put(CommandName.H4, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h4"))) );
// commandStates.put(CommandName.H5, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h5"))) commandStates.update(
// commandStates.put(CommandName.H6, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "h6"))) CommandName.H2,
// commandStates.put(CommandName.P, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "p"))) (val) => CommandState(formatCommandState!.executable,
// commandStates.put(CommandName.PRE, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "pre"))) isFormatActivated(formatCommandState, "h2")));
// commandStates.put(CommandName.BR, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, ""))) commandStates.update(
// commandStates.put(CommandName.BLOCKQUOTE, CommandState(formatCommandState.executable, isFormatActivated(formatCommandState, "blockquote"))) CommandName.H3,
// } (val) => CommandState(formatCommandState!.executable,
// isFormatActivated(formatCommandState, "h3")),
// commandStates[CommandName.INSERTHTML]?.let { insertHtmlState -> );
// commandStates.put(CommandName.INSERTLINK, insertHtmlState) commandStates.update(
// commandStates.put(CommandName.INSERTIMAGE, insertHtmlState) CommandName.H4,
// commandStates.put(CommandName.INSERTCHECKBOX, insertHtmlState) (val) => CommandState(formatCommandState!.executable,
// } isFormatActivated(formatCommandState, "h4")),
);
commandStates.update(
CommandName.H5,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h5")),
);
commandStates.update(
CommandName.H6,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "h6")),
);
commandStates.update(
CommandName.P,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "p")),
);
commandStates.update(
CommandName.PRE,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "pre")),
);
commandStates.update(
CommandName.BR,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "")),
);
commandStates.update(
CommandName.BLOCKQUOTE,
(val) => CommandState(formatCommandState!.executable,
isFormatActivated(formatCommandState, "blockquote")),
);
}
if (commandStates[CommandName.INSERTHTML] != null) {
CommandState? insertHtmlState = commandStates[CommandName.INSERTHTML];
commandStates.update(CommandName.INSERTLINK, (val) => insertHtmlState!);
commandStates.update(CommandName.INSERTIMAGE, (val) => insertHtmlState!);
commandStates.update(
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(
Map<CommandName, CommandState> commandStates) {
commandStatesChangedListeners.add(commandStates);
// listener.invoke(commandStates);
}
addDidHtmlChangeListener(DidHtmlChangeListener listener) {
didHtmlChangeListeners.add(listener);
}
addHtmlChangedListener(HtmlChangedListener listener) {
htmlChangedListeners.add(listener);
}
} }

View File

@ -16,11 +16,13 @@ class EditorToolBar extends StatelessWidget {
final Function(File image)? getImageUrl; final Function(File image)? getImageUrl;
final Function(File video)? getVideoUrl; final Function(File video)? getVideoUrl;
final JavascriptExecutorBase javascriptExecutor; final JavascriptExecutorBase javascriptExecutor;
final bool? enableVideo;
EditorToolBar({ EditorToolBar({
this.getImageUrl, this.getImageUrl,
this.getVideoUrl, this.getVideoUrl,
required this.javascriptExecutor, required this.javascriptExecutor,
this.enableVideo,
}); });
@override @override
@ -85,6 +87,14 @@ class EditorToolBar extends StatelessWidget {
} }
}, },
), ),
Visibility(
visible: false,
child: TabButton(
tooltip: 'Insert video',
icon: Icons.video_call_sharp,
onTap: () async {},
),
),
TabButton( TabButton(
tooltip: 'Underline', tooltip: 'Underline',
icon: Icons.format_underline, icon: Icons.format_underline,